[PATCH v2 2/2] ASoC: stm32: Add SPDIFRX support
Add SPDIFRX support to STM32. Signed-off-by: olivier moysan --- sound/soc/stm/Kconfig | 10 + sound/soc/stm/Makefile| 4 + sound/soc/stm/stm32_spdifrx.c | 998 ++ 3 files changed, 1012 insertions(+) create mode 100644 sound/soc/stm/stm32_spdifrx.c diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig index 23600a5..3398e6c 100644 --- a/sound/soc/stm/Kconfig +++ b/sound/soc/stm/Kconfig @@ -18,4 +18,14 @@ config SND_SOC_STM32_I2S help Say Y if you want to enable I2S for STM32 +config SND_SOC_STM32_SPDIFRX + tristate "STM32 S/PDIF receiver (SPDIFRX) support" + depends on ARCH_STM32 || COMPILE_TEST + depends on SND_SOC + select SND_SOC_GENERIC_DMAENGINE_PCM + select REGMAP_MMIO + select SND_SOC_SPDIF + help + Say Y if you want to enable S/PDIF capture for STM32 + endmenu diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile index 4140c67..4ed22e6 100644 --- a/sound/soc/stm/Makefile +++ b/sound/soc/stm/Makefile @@ -8,3 +8,7 @@ obj-$(CONFIG_SND_SOC_STM32_SAI) += snd-soc-stm32-sai.o # I2S snd-soc-stm32-i2s-objs := stm32_i2s.o obj-$(CONFIG_SND_SOC_STM32_I2S) += snd-soc-stm32-i2s.o + +# SPDIFRX +snd-soc-stm32-spdifrx-objs := stm32_spdifrx.o +obj-$(CONFIG_SND_SOC_STM32_SPDIFRX) += snd-soc-stm32-spdifrx.o diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c new file mode 100644 index 000..4e4250b --- /dev/null +++ b/sound/soc/stm/stm32_spdifrx.c @@ -0,0 +1,998 @@ +/* + * STM32 ALSA SoC Digital Audio Interface (SPDIF-rx) driver. + * + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author(s): Olivier Moysan for STMicroelectronics. + * + * License terms: GPL V2.0. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* SPDIF-rx Register Map */ +#define STM32_SPDIFRX_CR 0x00 +#define STM32_SPDIFRX_IMR 0x04 +#define STM32_SPDIFRX_SR 0x08 +#define STM32_SPDIFRX_IFCR 0x0C +#define STM32_SPDIFRX_DR 0x10 +#define STM32_SPDIFRX_CSR 0x14 +#define STM32_SPDIFRX_DIR 0x18 + +/* Bit definition for SPDIF_CR register */ +#define SPDIFRX_CR_SPDIFEN_SHIFT 0 +#define SPDIFRX_CR_SPDIFEN_MASKGENMASK(1, SPDIFRX_CR_SPDIFEN_SHIFT) +#define SPDIFRX_CR_SPDIFENSET(x) ((x) << SPDIFRX_CR_SPDIFEN_SHIFT) + +#define SPDIFRX_CR_RXDMAEN BIT(2) +#define SPDIFRX_CR_RXSTEO BIT(3) + +#define SPDIFRX_CR_DRFMT_SHIFT 4 +#define SPDIFRX_CR_DRFMT_MASK GENMASK(5, SPDIFRX_CR_DRFMT_SHIFT) +#define SPDIFRX_CR_DRFMTSET(x) ((x) << SPDIFRX_CR_DRFMT_SHIFT) + +#define SPDIFRX_CR_PMSKBIT(6) +#define SPDIFRX_CR_VMSKBIT(7) +#define SPDIFRX_CR_CUMSK BIT(8) +#define SPDIFRX_CR_PTMSK BIT(9) +#define SPDIFRX_CR_CBDMAEN BIT(10) +#define SPDIFRX_CR_CHSEL_SHIFT 11 +#define SPDIFRX_CR_CHSEL BIT(SPDIFRX_CR_CHSEL_SHIFT) + +#define SPDIFRX_CR_NBTR_SHIFT 12 +#define SPDIFRX_CR_NBTR_MASK GENMASK(13, SPDIFRX_CR_NBTR_SHIFT) +#define SPDIFRX_CR_NBTRSET(x) ((x) << SPDIFRX_CR_NBTR_SHIFT) + +#define SPDIFRX_CR_WFA BIT(14) + +#define SPDIFRX_CR_INSEL_SHIFT 16 +#define SPDIFRX_CR_INSEL_MASK GENMASK(18, PDIFRX_CR_INSEL_SHIFT) +#define SPDIFRX_CR_INSELSET(x) ((x) << SPDIFRX_CR_INSEL_SHIFT) + +#define SPDIFRX_CR_CKSEN_SHIFT 20 +#define SPDIFRX_CR_CKSEN BIT(20) +#define SPDIFRX_CR_CKSBKPENBIT(21) + +/* Bit definition for SPDIFRX_IMR register */ +#define SPDIFRX_IMR_RXNEI BIT(0) +#define SPDIFRX_IMR_CSRNEIEBIT(1) +#define SPDIFRX_IMR_PERRIE BIT(2) +#define SPDIFRX_IMR_OVRIE BIT(3) +#define SPDIFRX_IMR_SBLKIE BIT(4) +#define SPDIFRX_IMR_SYNCDIEBIT(5) +#define SPDIFRX_IMR_IFEIE BIT(6) + +#define SPDIFRX_XIMR_MASK GENMASK(6, 0) + +/* Bit definition for SPDIFRX_SR register */ +#define SPDIFRX_SR_RXNEBIT(0) +#define SPDIFRX_SR_CSRNE BIT(1) +#define SPDIFRX_SR_PERRBIT(2) +#define SPDIFRX_SR_OVR BIT(3) +#define SPDIFRX_SR_SBD BIT(4) +#define SPDIFRX_SR_SYNCD BIT(5) +#define SPDIFRX_SR_FERRBIT(6) +#define SPDIFRX_SR_SERRBIT(7) +#define SPDIFRX_SR_TERRBIT(8) + +#define SPDIFRX_SR_WIDTH5_SHIFT16 +#define SPDIFRX_SR_WIDTH5_MASK GENMASK(30, PDIFRX_SR_WIDTH5_SHIFT) +#define SPDIFRX_SR_WIDTH5SET(x)((x) << SPDIFRX_SR_WIDTH5_SHIFT) + +/* Bit definition for SPDIFRX_IFCR register
[PATCH v2 1/2] ASoC: stm32: Add DT bindings for SPDIFRX interface
Add documentation of device tree bindings for the STM32 SPDIFRX interface. Signed-off-by: olivier moysan --- .../devicetree/bindings/sound/st,stm32-spdifrx.txt | 56 ++ 1 file changed, 56 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-spdifrx.txt diff --git a/Documentation/devicetree/bindings/sound/st,stm32-spdifrx.txt b/Documentation/devicetree/bindings/sound/st,stm32-spdifrx.txt new file mode 100644 index 000..33826f2 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/st,stm32-spdifrx.txt @@ -0,0 +1,56 @@ +STMicroelectronics STM32 S/PDIF receiver (SPDIFRX). + +The SPDIFRX peripheral, is designed to receive an S/PDIF flow compliant with +IEC-60958 and IEC-61937. + +Required properties: + - compatible: should be "st,stm32h7-spdifrx" + - reg: cpu DAI IP base address and size + - clocks: must contain an entry for kclk (used as S/PDIF signal reference) + - clock-names: must contain "kclk" + - interrupts: cpu DAI interrupt line + - dmas: DMA specifiers for audio data DMA and iec control flow DMA +See STM32 DMA bindings, Documentation/devicetree/bindings/dma/stm32-dma.txt + - dma-names: two dmas have to be defined, "rx" and "rx-ctrl" + +Optional properties: + - resets: Reference to a reset controller asserting the SPDIFRX + +The device node should contain one 'port' child node with one child 'endpoint' +node, according to the bindings defined in Documentation/devicetree/bindings/ +graph.txt. + +Example: +spdifrx: spdifrx@40004000 { + compatible = "st,stm32h7-spdifrx"; + reg = <0x40004000 0x400>; + clocks = <&rcc SPDIFRX_CK>; + clock-names = "kclk"; + interrupts = <97>; + dmas = <&dmamux1 2 93 0x400 0x0>, + <&dmamux1 3 94 0x400 0x0>; + dma-names = "rx", "rx-ctrl"; + pinctrl-0 = <&spdifrx_pins>; + pinctrl-names = "default"; + + spdifrx_port: port { + cpu_endpoint: endpoint { + remote-endpoint = <&codec_endpoint>; + }; + }; +}; + +spdif_in: spdif-in { + compatible = "linux,spdif-dir"; + + codec_port: port { + codec_endpoint: endpoint { + remote-endpoint = <&cpu_endpoint>; + }; + }; +}; + +soundcard { + compatible = "audio-graph-card"; + dais = <&spdifrx_port>; +}; -- 1.9.1
[PATCH v2 0/2] ASoC: stm32: add SPDIFRX driver
This patch-set handles the SPDIFRX on STM32 platforms. The SPDIFRX peripheral, is designed to receive an S/PDIF flow compliant with IEC-60958 and IEC-61937 standards. SPDIFRX uses two DMA channels: - one DMA channel for S/PDIF data stream. - one DMA channel for control flow (channel status and user data bits). DMA is used for control flow instead of interrupts to get better performances, as channel status and user data are retrieved byte per byte. v1 -> v2: - fix merge issue olivier moysan (2): ASoC: stm32: Add DT bindings for SPDIFRX interface ASoC: stm32: Add SPDIFRX support .../devicetree/bindings/sound/st,stm32-spdifrx.txt | 56 ++ sound/soc/stm/Kconfig | 10 + sound/soc/stm/Makefile | 4 + sound/soc/stm/stm32_spdifrx.c | 998 + 4 files changed, 1068 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-spdifrx.txt create mode 100644 sound/soc/stm/stm32_spdifrx.c -- 1.9.1
Re: [PATCH 14/15] sound: stm32: use devm_of_platform_populate()
Acked-by: Olivier Moysan
[PATCH 1/1] ASoC: stm32: sai: fix warning in stm32_sai_set_config()
Fix uninitialized warning introduced by "Move static settings to DAI init" commit in stm32_sai_set_config() function. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_sai_sub.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 150ad54..08583b9 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -482,13 +482,13 @@ static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai, cr1_mask = SAI_XCR1_DS_MASK; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: - cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_8); + cr1 = SAI_XCR1_DS_SET(SAI_DATASIZE_8); break; case SNDRV_PCM_FORMAT_S16_LE: - cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_16); + cr1 = SAI_XCR1_DS_SET(SAI_DATASIZE_16); break; case SNDRV_PCM_FORMAT_S32_LE: - cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_32); + cr1 = SAI_XCR1_DS_SET(SAI_DATASIZE_32); break; default: dev_err(cpu_dai->dev, "Data format not supported"); -- 1.9.1
Re: [INTERNAL][PATCH 4/7] ASoC: stm32: sai: fix stop management in isr
Hello Takashi, Sorry for late answer. I was OoO. Ok, I will add a protection on sai->substream accesses. Best regards Olivier On 10/26/2017 05:32 PM, Takashi Iwai wrote: > On Thu, 19 Oct 2017 15:03:20 +0200, > Olivier Moysan wrote: >> >> Add check on substream validity. >> >> Signed-off-by: Olivier Moysan >> --- >> sound/soc/stm/stm32_sai_sub.c | 12 >> 1 file changed, 8 insertions(+), 4 deletions(-) >> >> diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c >> index 2af397d..815ef10 100644 >> --- a/sound/soc/stm/stm32_sai_sub.c >> +++ b/sound/soc/stm/stm32_sai_sub.c >> @@ -184,7 +184,6 @@ static bool stm32_sai_sub_writeable_reg(struct device >> *dev, unsigned int reg) >> static irqreturn_t stm32_sai_isr(int irq, void *devid) >> { >> struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid; >> -struct snd_pcm_substream *substream = sai->substream; >> struct platform_device *pdev = sai->pdev; >> unsigned int sr, imr, flags; >> snd_pcm_state_t status = SNDRV_PCM_STATE_RUNNING; >> @@ -199,6 +198,11 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid) >> regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK, >> SAI_XCLRFR_MASK); >> >> +if (!sai->substream) { >> +dev_err(&pdev->dev, "Device stopped. Spurious IRQ 0x%x\n", sr); >> +return IRQ_NONE; >> +} >> + >> if (flags & SAI_XIMR_OVRUDRIE) { >> dev_err(&pdev->dev, "IRQ %s\n", >> STM_SAI_IS_PLAYBACK(sai) ? "underrun" : "overrun"); >> @@ -227,9 +231,9 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid) >> } >> >> if (status != SNDRV_PCM_STATE_RUNNING) { >> -snd_pcm_stream_lock(substream); >> -snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); >> -snd_pcm_stream_unlock(substream); >> +snd_pcm_stream_lock(sai->substream); >> +snd_pcm_stop(sai->substream, SNDRV_PCM_STATE_XRUN); >> +snd_pcm_stream_unlock(sai->substream); > > Actually changing to sai->substream opens a race, so this chunk is a > bad move, at least. We have no protection of sai->substream in this > context, thus it can hit a NULL dereference... > > > thanks, > > Takashi >
[PATCH 0/2] ASoC: stm32: spdifrx: DMA management fixes
This patch-set gathers DMA management fixes for STM32 SPDIFRX. Olivier Moysan (2): ASoC: stm32: spdifrx: fix 16 bits capture ASoC: stm32: spdifrx: fix control DMA error management sound/soc/stm/stm32_spdifrx.c | 23 +-- 1 file changed, 13 insertions(+), 10 deletions(-) -- 1.9.1
[PATCH 2/2] ASoC: stm32: spdifrx: fix control DMA error management
Fix DMA channel request error handling. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_spdifrx.c | 15 +++ 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index d7dbe84..b9bdefc 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -392,6 +392,12 @@ static int stm32_spdifrx_dma_ctrl_register(struct device *dev, { int ret; + spdifrx->ctrl_chan = dma_request_chan(dev, "rx-ctrl"); + if (IS_ERR(spdifrx->ctrl_chan)) { + dev_err(dev, "dma_request_slave_channel failed\n"); + return PTR_ERR(spdifrx->ctrl_chan); + } + spdifrx->dmab = devm_kzalloc(dev, sizeof(struct snd_dma_buffer), GFP_KERNEL); if (!spdifrx->dmab) @@ -406,12 +412,6 @@ static int stm32_spdifrx_dma_ctrl_register(struct device *dev, return ret; } - spdifrx->ctrl_chan = dma_request_chan(dev, "rx-ctrl"); - if (!spdifrx->ctrl_chan) { - dev_err(dev, "dma_request_slave_channel failed\n"); - return -EINVAL; - } - spdifrx->slave_config.direction = DMA_DEV_TO_MEM; spdifrx->slave_config.src_addr = (dma_addr_t)(spdifrx->phys_addr + STM32_SPDIFRX_CSR); @@ -423,7 +423,6 @@ static int stm32_spdifrx_dma_ctrl_register(struct device *dev, &spdifrx->slave_config); if (ret < 0) { dev_err(dev, "dmaengine_slave_config returned error %d\n", ret); - dma_release_channel(spdifrx->ctrl_chan); spdifrx->ctrl_chan = NULL; } @@ -962,7 +961,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) return 0; error: - if (spdifrx->ctrl_chan) + if (!IS_ERR(spdifrx->ctrl_chan)) dma_release_channel(spdifrx->ctrl_chan); if (spdifrx->dmab) snd_dma_free_pages(spdifrx->dmab); -- 1.9.1
[PATCH 1/2] ASoC: stm32: spdifrx: fix 16 bits capture
Change DMA bus width to manage properly 16 bits packed format. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_spdifrx.c | 8 ++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index 84cc567..d7dbe84 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -750,17 +750,21 @@ static int stm32_spdifrx_hw_params(struct snd_pcm_substream *substream, switch (data_size) { case 16: fmt = SPDIFRX_DRFMT_PACKED; - spdifrx->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; break; case 32: fmt = SPDIFRX_DRFMT_LEFT; - spdifrx->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; break; default: dev_err(&spdifrx->pdev->dev, "Unexpected data format\n"); return -EINVAL; } + /* +* Set buswidth to 4 bytes for all data formats. +* Packed format: transfer 2 x 2 bytes samples +* Left format: transfer 1 x 3 bytes samples + 1 dummy byte +*/ + spdifrx->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; snd_soc_dai_init_dma_data(cpu_dai, NULL, &spdifrx->dma_params); return regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, -- 1.9.1
[INTERNAL][PATCH 0/7] ASoC: stm32: Add SAI synchronization support
This patch-set adds support of synchronization features for SAI interface. It also adds minor fixes and improvements. Olivier Moysan (7): ASoC: stm32: Add synchronization to SAI bindings ASoC: stm32: sai: Move static settings to DAI init ASoC: stm32: sai: Fix DMA burst size ASoC: stm32: sai: fix stop management in isr ASoC: stm32: sai: Remove spurious IRQs on stop ASoC: stm32: sai: Fix get reset controller ASoC: stm32: sai: Add synchronization support .../devicetree/bindings/sound/st,stm32-sai.txt | 14 +- sound/soc/stm/stm32_sai.c | 162 - sound/soc/stm/stm32_sai.h | 22 ++- sound/soc/stm/stm32_sai_sub.c | 156 +--- 4 files changed, 318 insertions(+), 36 deletions(-) -- 1.9.1
[INTERNAL][PATCH 5/7] ASoC: stm32: sai: Remove spurious IRQs on stop
Clear IRQ mask on stream stop to avoid spurious IRQs. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_sai_sub.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 815ef10..fd7dc77 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -419,8 +419,6 @@ static int stm32_sai_startup(struct snd_pcm_substream *substream, } /* Enable ITs */ - regmap_update_bits(sai->regmap, STM_SAI_SR_REGX, - SAI_XSR_MASK, (unsigned int)~SAI_XSR_MASK); regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK, SAI_XCLRFR_MASK); @@ -693,6 +691,9 @@ static int stm32_sai_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_STOP: dev_dbg(cpu_dai->dev, "Disable DMA and SAI\n"); + regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, + SAI_XIMR_MASK, 0); + regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_SAIEN, (unsigned int)~SAI_XCR1_SAIEN); -- 1.9.1
[INTERNAL][PATCH 4/7] ASoC: stm32: sai: fix stop management in isr
Add check on substream validity. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_sai_sub.c | 12 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 2af397d..815ef10 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -184,7 +184,6 @@ static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg) static irqreturn_t stm32_sai_isr(int irq, void *devid) { struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid; - struct snd_pcm_substream *substream = sai->substream; struct platform_device *pdev = sai->pdev; unsigned int sr, imr, flags; snd_pcm_state_t status = SNDRV_PCM_STATE_RUNNING; @@ -199,6 +198,11 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid) regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK, SAI_XCLRFR_MASK); + if (!sai->substream) { + dev_err(&pdev->dev, "Device stopped. Spurious IRQ 0x%x\n", sr); + return IRQ_NONE; + } + if (flags & SAI_XIMR_OVRUDRIE) { dev_err(&pdev->dev, "IRQ %s\n", STM_SAI_IS_PLAYBACK(sai) ? "underrun" : "overrun"); @@ -227,9 +231,9 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid) } if (status != SNDRV_PCM_STATE_RUNNING) { - snd_pcm_stream_lock(substream); - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock(substream); + snd_pcm_stream_lock(sai->substream); + snd_pcm_stop(sai->substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock(sai->substream); } return IRQ_HANDLED; -- 1.9.1
[INTERNAL][PATCH 3/7] ASoC: stm32: sai: Fix DMA burst size
Set best burst size tradeoff for 8, 16, 32 bits transfers. Signed-off-by: olivier moysan --- sound/soc/stm/stm32_sai_sub.c | 21 + 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index fdc1891..2af397d 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -445,12 +445,16 @@ static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai, { struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); int cr1, cr1_mask, ret; - int fth = STM_SAI_FIFO_TH_HALF; - /* FIFO config */ + /* +* DMA bursts increment is set to 4 words. +* SAI fifo threshold is set to half fifo, to keep enough space +* for DMA incoming bursts. +*/ regmap_update_bits(sai->regmap, STM_SAI_CR2_REGX, SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK, - SAI_XCR2_FFLUSH | SAI_XCR2_FTH_SET(fth)); + SAI_XCR2_FFLUSH | + SAI_XCR2_FTH_SET(STM_SAI_FIFO_TH_HALF)); /* Mode, data format and channel config */ cr1_mask = SAI_XCR1_DS_MASK; @@ -479,10 +483,6 @@ static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai, return ret; } - /* DMA config */ - sai->dma_params.maxburst = STM_SAI_FIFO_SIZE * fth / sizeof(u32); - snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)&sai->dma_params); - return 0; } @@ -726,7 +726,12 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) int cr1 = 0, cr1_mask; sai->dma_params.addr = (dma_addr_t)(sai->phys_addr + STM_SAI_DR_REGX); - sai->dma_params.maxburst = 1; + /* +* DMA supports 4, 8 or 16 burst sizes. Burst size 4 is the best choice, +* as it allows bytes, half-word and words transfers. (See DMA fifos +* constraints). +*/ + sai->dma_params.maxburst = 4; /* Buswidth will be set by framework at runtime */ sai->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; -- 1.9.1
[INTERNAL][PATCH 1/7] ASoC: stm32: Add synchronization to SAI bindings
Add synchronization configuration to STM32 SAI bindings. This patch also adds peripheral clock which is required to access synchronization register. Signed-off-by: Olivier Moysan --- Documentation/devicetree/bindings/sound/st,stm32-sai.txt | 14 +++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/st,stm32-sai.txt b/Documentation/devicetree/bindings/sound/st,stm32-sai.txt index f1c5ae5..1f9cd70 100644 --- a/Documentation/devicetree/bindings/sound/st,stm32-sai.txt +++ b/Documentation/devicetree/bindings/sound/st,stm32-sai.txt @@ -10,13 +10,21 @@ Required properties: - reg: Base address and size of SAI common register set. - clocks: Must contain phandle and clock specifier pairs for each entry in clock-names. - - clock-names: Must contain "x8k" and "x11k" + - clock-names: Must contain "pclk" "x8k" and "x11k" + "pclk": Clock which feeds the peripheral bus interface. + Mandatory for "st,stm32h7-sai" compatible. + Not used for "st,stm32f4-sai" compatible. "x8k": SAI parent clock for sampling rates multiple of 8kHz. "x11k": SAI parent clock for sampling rates multiple of 11.025kHz. - interrupts: cpu DAI interrupt line shared by SAI sub-blocks Optional properties: - resets: Reference to a reset controller asserting the SAI + - st,sync: specify synchronization mode. + By default SAI sub-block is in asynchronous mode. + This property sets SAI sub-block as slave of another SAI sub-block. + Must contain the phandle and index of the sai sub-block providing + the synchronization. SAI subnodes: Two subnodes corresponding to SAI sub-block instances A et B can be defined. @@ -52,8 +60,8 @@ sai1: sai1@40015800 { #size-cells = <1>; ranges = <0 0x40015800 0x400>; reg = <0x40015800 0x4>; - clocks = <&rcc PLL1_Q>, <&rcc PLL2_P>; - clock-names = "x8k", "x11k"; + clocks = <&rcc SAI1_CK>, <&rcc PLL1_Q>, <&rcc PLL2_P>; + clock-names = "pclk", "x8k", "x11k"; interrupts = <87>; sai1a: audio-controller@40015804 { -- 1.9.1
[INTERNAL][PATCH 2/7] ASoC: stm32: sai: Move static settings to DAI init
Audio interface direction and protocol settings does not change at runtime. So, these settings are moved from hw_params function to dai_probe and set_fmt. Signed-off-by: olivier moysan --- sound/soc/stm/stm32_sai_sub.c | 23 +-- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 90d4396..fdc1891 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -304,12 +304,15 @@ static int stm32_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); - int cr1 = 0, frcr = 0; - int cr1_mask = 0, frcr_mask = 0; + int cr1, frcr = 0; + int cr1_mask, frcr_mask = 0; int ret; dev_dbg(cpu_dai->dev, "fmt %x\n", fmt); + cr1_mask = SAI_XCR1_PRTCFG_MASK; + cr1 = SAI_XCR1_PRTCFG_SET(SAI_FREE_PROTOCOL); + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { /* SCK active high for all protocols */ case SND_SOC_DAIFMT_I2S: @@ -336,7 +339,7 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) return -EINVAL; } - cr1_mask |= SAI_XCR1_PRTCFG_MASK | SAI_XCR1_CKSTR; + cr1_mask |= SAI_XCR1_CKSTR; frcr_mask |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF | SAI_XFRCR_FSDEF; @@ -450,7 +453,7 @@ static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai, SAI_XCR2_FFLUSH | SAI_XCR2_FTH_SET(fth)); /* Mode, data format and channel config */ - cr1 = SAI_XCR1_PRTCFG_SET(SAI_FREE_PROTOCOL); + cr1_mask = SAI_XCR1_DS_MASK; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_8); @@ -465,11 +468,6 @@ static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai, dev_err(cpu_dai->dev, "Data format not supported"); return -EINVAL; } - cr1_mask = SAI_XCR1_DS_MASK | SAI_XCR1_PRTCFG_MASK; - - cr1_mask |= SAI_XCR1_RX_TX; - if (STM_SAI_IS_CAPTURE(sai)) - cr1 |= SAI_XCR1_RX_TX; cr1_mask |= SAI_XCR1_MONO; if ((sai->slots == 2) && (params_channels(params) == 1)) @@ -725,6 +723,7 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream, static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) { struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); + int cr1 = 0, cr1_mask; sai->dma_params.addr = (dma_addr_t)(sai->phys_addr + STM_SAI_DR_REGX); sai->dma_params.maxburst = 1; @@ -736,7 +735,11 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) else snd_soc_dai_init_dma_data(cpu_dai, NULL, &sai->dma_params); - return 0; + cr1_mask = SAI_XCR1_RX_TX; + if (STM_SAI_IS_CAPTURE(sai)) + cr1 |= SAI_XCR1_RX_TX; + + return regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1); } static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = { -- 1.9.1
[INTERNAL][PATCH 6/7] ASoC: stm32: sai: Fix get reset controller
Use devm version of reset_control_get function to manage driver removing properly. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_sai.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index 1258bef..5fe878ac 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -85,7 +85,7 @@ static int stm32_sai_probe(struct platform_device *pdev) } /* reset */ - rst = reset_control_get_exclusive(&pdev->dev, NULL); + rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (!IS_ERR(rst)) { reset_control_assert(rst); udelay(2); -- 1.9.1
[INTERNAL][PATCH 7/7] ASoC: stm32: sai: Add synchronization support
Add Synchronization support for STM32 SAI. Signed-off-by: olivier moysan --- sound/soc/stm/stm32_sai.c | 160 -- sound/soc/stm/stm32_sai.h | 22 +- sound/soc/stm/stm32_sai_sub.c | 95 + 3 files changed, 269 insertions(+), 8 deletions(-) diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index 5fe878ac..d6f71a3 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -16,6 +16,7 @@ * details. */ +#include #include #include #include @@ -27,6 +28,16 @@ #include "stm32_sai.h" +static LIST_HEAD(sync_providers); +static DEFINE_MUTEX(sync_mutex); + +struct sync_provider { + struct list_head link; + struct device_node *node; + int (*sync_conf)(void *data, int synco); + void *data; +}; + static const struct stm32_sai_conf stm32_sai_conf_f4 = { .version = SAI_STM32F4, }; @@ -41,23 +52,143 @@ {} }; +static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci) +{ + int ret; + + /* Enable peripheral clock to allow GCR register access */ + ret = clk_prepare_enable(sai->pclk); + if (ret) { + dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret); + return ret; + } + + writel_relaxed(FIELD_PREP(SAI_GCR_SYNCIN_MASK, (synci - 1)), sai->base); + + clk_disable_unprepare(sai->pclk); + + return 0; +} + +static int stm32_sai_sync_conf_provider(void *data, int synco) +{ + struct stm32_sai_data *sai = (struct stm32_sai_data *)data; + u32 prev_synco; + int ret; + + /* Enable peripheral clock to allow GCR register access */ + ret = clk_prepare_enable(sai->pclk); + if (ret) { + dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret); + return ret; + } + + dev_dbg(&sai->pdev->dev, "Set %s%s as synchro provider\n", + sai->pdev->dev.of_node->name, + synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); + + prev_synco = FIELD_GET(SAI_GCR_SYNCOUT_MASK, readl_relaxed(sai->base)); + if (prev_synco != STM_SAI_SYNC_OUT_NONE && synco != prev_synco) { + dev_err(&sai->pdev->dev, "%s%s already set as sync provider\n", + sai->pdev->dev.of_node->name, + prev_synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); + clk_disable_unprepare(sai->pclk); + return -EINVAL; + } + + writel_relaxed(FIELD_PREP(SAI_GCR_SYNCOUT_MASK, synco), sai->base); + + clk_disable_unprepare(sai->pclk); + + return 0; +} + +static int stm32_sai_set_sync_provider(struct device_node *np, int synco) +{ + struct sync_provider *provider; + int ret; + + mutex_lock(&sync_mutex); + list_for_each_entry(provider, &sync_providers, link) { + if (provider->node == np) { + ret = provider->sync_conf(provider->data, synco); + mutex_unlock(&sync_mutex); + return ret; + } + } + mutex_unlock(&sync_mutex); + + /* SAI sync provider not found */ + return -ENODEV; +} + +static int stm32_sai_set_sync(struct stm32_sai_data *sai, + struct device_node *np_provider, + int synco, int synci) +{ + int ret; + + /* Configure sync client */ + stm32_sai_sync_conf_client(sai, synci); + + /* Configure sync provider */ + ret = stm32_sai_set_sync_provider(np_provider, synco); + + return ret; +} + +static int stm32_sai_sync_add_provider(struct platform_device *pdev, + void *data) +{ + struct sync_provider *sp; + + sp = devm_kzalloc(&pdev->dev, sizeof(*sp), GFP_KERNEL); + if (!sp) + return -ENOMEM; + + sp->node = of_node_get(pdev->dev.of_node); + sp->data = data; + sp->sync_conf = &stm32_sai_sync_conf_provider; + + mutex_lock(&sync_mutex); + list_add(&sp->link, &sync_providers); + mutex_unlock(&sync_mutex); + + return 0; +} + +static void stm32_sai_sync_del_provider(struct device_node *np) +{ + struct sync_provider *sp; + + mutex_lock(&sync_mutex); + list_for_each_entry(sp, &sync_providers, link) { + if (sp->node == np) { + list_del(&sp->link); + of_node_put(sp->node); + break; + } + } + mutex_unlock(&sync_mutex); +} + static int stm32_sai_probe(struct platform_device *pdev) { struct device_node *np =
Re: [INTERNAL][PATCH 0/7] ASoC: stm32: Add SAI synchronization support
Please ignore "[INTERNAL]" in subject. Sorry for this unappropriated header. On 10/19/2017 03:03 PM, Olivier Moysan wrote: > This patch-set adds support of synchronization features for SAI interface. > It also adds minor fixes and improvements. > > Olivier Moysan (7): >ASoC: stm32: Add synchronization to SAI bindings >ASoC: stm32: sai: Move static settings to DAI init >ASoC: stm32: sai: Fix DMA burst size >ASoC: stm32: sai: fix stop management in isr >ASoC: stm32: sai: Remove spurious IRQs on stop >ASoC: stm32: sai: Fix get reset controller >ASoC: stm32: sai: Add synchronization support > > .../devicetree/bindings/sound/st,stm32-sai.txt | 14 +- > sound/soc/stm/stm32_sai.c | 162 > - > sound/soc/stm/stm32_sai.h | 22 ++- > sound/soc/stm/stm32_sai_sub.c | 156 > +--- > 4 files changed, 318 insertions(+), 36 deletions(-) >
[PATCH v3 3/3] ASoC: stm32: Add full duplex support to i2s
This patch allows to use i2s interface either as single audio path (rx or tx), or bidirectional audio path. This patch is added separately, as the driver does not follow recommended use of the interface, to support this configuration. Signed-off-by: olivier moysan --- sound/soc/stm/stm32_i2s.c | 87 +-- 1 file changed, 46 insertions(+), 41 deletions(-) diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 22152a1..8052629 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -489,7 +489,6 @@ static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai, struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); int format = params_width(params); u32 cfgr, cfgr_mask, cfg1, cfg1_mask; - bool playback_flg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); unsigned int fthlv; int ret; @@ -515,19 +514,13 @@ static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai, } if (STM32_I2S_IS_SLAVE(i2s)) { - if (playback_flg) - cfgr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_TX_SLAVE); - else - cfgr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_RX_SLAVE); + cfgr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_FD_SLAVE); /* As data length is either 16 or 32 bits, fixch always set */ cfgr |= I2S_CGFR_FIXCH; cfgr_mask |= I2S_CGFR_FIXCH; } else { - if (playback_flg) - cfgr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_TX_MASTER); - else - cfgr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_RX_MASTER); + cfgr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_FD_MASTER); } cfgr_mask |= I2S_CGFR_I2SCFG_MASK; @@ -536,9 +529,7 @@ static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai, if (ret < 0) return ret; - cfg1 = I2S_CFG1_RXDMAEN; - if (playback_flg) - cfg1 = I2S_CFG1_TXDMAEN; + cfg1 = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN; cfg1_mask = cfg1; fthlv = STM32_I2S_FIFO_SIZE * I2S_FIFO_TH_ONE_QUARTER / 4; @@ -553,32 +544,15 @@ static int stm32_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); - int ret, ier; i2s->substream = substream; spin_lock(&i2s->lock_fd); - if (i2s->refcount) { - dev_err(cpu_dai->dev, "%s stream already started\n", - (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? - "Capture" : "Playback")); - spin_unlock(&i2s->lock_fd); - return -EBUSY; - } - i2s->refcount = 1; + i2s->refcount++; spin_unlock(&i2s->lock_fd); - ret = regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, -I2S_IFCR_MASK, I2S_IFCR_MASK); - if (ret < 0) - return ret; - - /* Enable ITs */ - ier = I2S_IER_OVRIE | I2S_IER_UDRIE; - if (STM32_I2S_IS_SLAVE(i2s)) - ier |= I2S_IER_TIFREIE; - - return regmap_update_bits(i2s->regmap, STM32_I2S_IER_REG, ier, ier); + return regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, + I2S_IFCR_MASK, I2S_IFCR_MASK); } static int stm32_i2s_hw_params(struct snd_pcm_substream *substream, @@ -605,7 +579,7 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, { struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); bool playback_flg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); - u32 cfg1_mask; + u32 cfg1_mask, ier; int ret; switch (cmd) { @@ -628,10 +602,48 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, dev_err(cpu_dai->dev, "Error %d starting I2S\n", ret); return ret; } + + regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, + I2S_IFCR_MASK, I2S_IFCR_MASK); + + if (playback_flg) { + ier = I2S_IER_UDRIE; + } else { + ier = I2S_IER_OVRIE; + + spin_lock(&i2s->lock_fd); + if (i2s->refcount == 1) + /* dummy write to trigger capture */ + regmap_write(i2s->regmap, +STM32_I2S_TXDR_REG, 0); + spin_unlock(&i2s->lock_fd); + } + + if (STM32_I2S_IS_SLAVE(i2s)) + ier |= I2S_IE
[PATCH v3 0/3] Add I2S driver
This patch-set handles the SPI/I2S IP on STM32 platforms. It applies to STM32 platforms implementing version 2 of SPI/I2S IP like STM32H7 series. The SPI/I2S block is a serial peripheral interface (SPI), which can also be configured to work on I2S/PCM mode. ASoC STM32 I2S driver only supports this I2S/PCM mode. The I2S/PCM interface can either support full duplex or half-duplex communication (transmitter or receiver only). The change of mode requires to stop the interface. This means that the two audio paths cannot be basically managed independently. The driver supports playback and capture through a single bidirectionnal DAI. In the first version of the driver, playback and capture paths are made mutually exclusive. A second patch introduces support of full duplex mode. In this implementation the interface is systematically configured in full-duplex mode from hardware point of view. So, when managing a single audio path, the interface is not configured as transmit or receive only, as it should be. This restriction is handled by masking underrun/overrun errors from the unused path. The I2S/PCM interface supports four audio standards: I2S Philips standard, MSB justified standard, LSB justified standard, PCM standard PCM standard is declined in two versions, PCM short and long, according frame synchronization duration. These standards can be mapped to ASoC standards as follows: - I2S: i2s - MSB justified standard: left_j - LSB justified standard: right_j - PCM short: dsp_a PCM long format is not exposed as it does not match supported ASoC standards. v2 -> v3 - fix spinlock handling olivier moysan (3): dt-bindings: Document STM32 I2S bindings ASoC: stm32: Add I2S driver ASoC: stm32: Add full duplex support to i2s .../devicetree/bindings/sound/st,stm32-i2s.txt | 68 ++ sound/soc/stm/Kconfig | 2 +- sound/soc/stm/Makefile | 4 + sound/soc/stm/stm32_i2s.c | 946 + 4 files changed, 1019 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-i2s.txt create mode 100644 sound/soc/stm/stm32_i2s.c -- 1.9.1
[PATCH v3 2/3] ASoC: stm32: Add I2S driver
Add I2S ASoC driver for STM32. This version of the driver supports only exclusive playback and capture interface. Signed-off-by: olivier moysan --- sound/soc/stm/Kconfig | 2 +- sound/soc/stm/Makefile| 4 + sound/soc/stm/stm32_i2s.c | 941 ++ 3 files changed, 946 insertions(+), 1 deletion(-) create mode 100644 sound/soc/stm/stm32_i2s.c diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig index 972970f..a6372de 100644 --- a/sound/soc/stm/Kconfig +++ b/sound/soc/stm/Kconfig @@ -5,4 +5,4 @@ menuconfig SND_SOC_STM32 select SND_SOC_GENERIC_DMAENGINE_PCM select REGMAP_MMIO help - Say Y if you want to enable ASoC-support for STM32 + Say Y if you want to enable ASoC support for STM32 diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile index e466a47..8251931 100644 --- a/sound/soc/stm/Makefile +++ b/sound/soc/stm/Makefile @@ -4,3 +4,7 @@ obj-$(CONFIG_SND_SOC_STM32) += snd-soc-stm32-sai-sub.o snd-soc-stm32-sai-objs := stm32_sai.o obj-$(CONFIG_SND_SOC_STM32) += snd-soc-stm32-sai.o + +# I2S +snd-soc-stm32-i2s-objs := stm32_i2s.o +obj-$(CONFIG_SND_SOC_STM32) += snd-soc-stm32-i2s.o diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c new file mode 100644 index 000..22152a1 --- /dev/null +++ b/sound/soc/stm/stm32_i2s.c @@ -0,0 +1,941 @@ +/* + * STM32 ALSA SoC Digital Audio Interface (I2S) driver. + * + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author(s): Olivier Moysan for STMicroelectronics. + * + * License terms: GPL V2.0. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define STM32_I2S_CR1_REG 0x0 +#define STM32_I2S_CFG1_REG 0x08 +#define STM32_I2S_CFG2_REG 0x0C +#define STM32_I2S_IER_REG 0x10 +#define STM32_I2S_SR_REG 0x14 +#define STM32_I2S_IFCR_REG 0x18 +#define STM32_I2S_TXDR_REG 0X20 +#define STM32_I2S_RXDR_REG 0x30 +#define STM32_I2S_CGFR_REG 0X50 + +/* Bit definition for SPI2S_CR1 register */ +#define I2S_CR1_SPEBIT(0) +#define I2S_CR1_CSTART BIT(9) +#define I2S_CR1_CSUSP BIT(10) +#define I2S_CR1_HDDIR BIT(11) +#define I2S_CR1_SSIBIT(12) +#define I2S_CR1_CRC33_17 BIT(13) +#define I2S_CR1_RCRCI BIT(14) +#define I2S_CR1_TCRCI BIT(15) + +/* Bit definition for SPI_CFG2 register */ +#define I2S_CFG2_IOSWP_SHIFT 15 +#define I2S_CFG2_IOSWP BIT(I2S_CFG2_IOSWP_SHIFT) +#define I2S_CFG2_LSBFRST BIT(23) +#define I2S_CFG2_AFCNTRBIT(31) + +/* Bit definition for SPI_CFG1 register */ +#define I2S_CFG1_FTHVL_SHIFT 5 +#define I2S_CFG1_FTHVL_MASKGENMASK(8, I2S_CFG1_FTHVL_SHIFT) +#define I2S_CFG1_FTHVL_SET(x) ((x) << I2S_CFG1_FTHVL_SHIFT) + +#define I2S_CFG1_TXDMAEN BIT(15) +#define I2S_CFG1_RXDMAEN BIT(14) + +/* Bit definition for SPI2S_IER register */ +#define I2S_IER_RXPIE BIT(0) +#define I2S_IER_TXPIE BIT(1) +#define I2S_IER_DPXPIE BIT(2) +#define I2S_IER_EOTIE BIT(3) +#define I2S_IER_TXTFIE BIT(4) +#define I2S_IER_UDRIE BIT(5) +#define I2S_IER_OVRIE BIT(6) +#define I2S_IER_CRCEIE BIT(7) +#define I2S_IER_TIFREIEBIT(8) +#define I2S_IER_MODFIE BIT(9) +#define I2S_IER_TSERFIEBIT(10) + +/* Bit definition for SPI2S_SR register */ +#define I2S_SR_RXP BIT(0) +#define I2S_SR_TXP BIT(1) +#define I2S_SR_DPXPBIT(2) +#define I2S_SR_EOT BIT(3) +#define I2S_SR_TXTFBIT(4) +#define I2S_SR_UDR BIT(5) +#define I2S_SR_OVR BIT(6) +#define I2S_SR_CRCERR BIT(7) +#define I2S_SR_TIFRE BIT(8) +#define I2S_SR_MODFBIT(9) +#define I2S_SR_TSERF BIT(10) +#define I2S_SR_SUSPBIT(11) +#define I2S_SR_TXC BIT(12) +#define I2S_SR_RXPLVL GENMASK(14, 13) +#define I2S_SR_RXWNE BIT(15) + +#define I2S_SR_MASKGENMASK(15, 0) + +/* Bit definition for SPI_IFCR register */ +#define I2S_IFCR_EOTC BIT(3) +#define I2S_IFCR_TXTFC BIT(4) +#define I2S_IFCR_UDRC BIT(5) +#define I2S_IFCR_OVRC BIT(6) +#define I2S_IFCR_CRCEC BIT(7) +#define I2S_IFCR_TIFRECBIT(8) +#define I2S_IFCR_MODFC BIT(9) +#define I2S_IFCR_TSERFCBIT(10) +#define I2S_IFCR_SUSPC BIT(11) + +#
[PATCH v3 1/3] dt-bindings: Document STM32 I2S bindings
Add documentation of device tree bindings for STM32 SPI/I2S. Signed-off-by: olivier moysan --- .../devicetree/bindings/sound/st,stm32-i2s.txt | 68 ++ 1 file changed, 68 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-i2s.txt diff --git a/Documentation/devicetree/bindings/sound/st,stm32-i2s.txt b/Documentation/devicetree/bindings/sound/st,stm32-i2s.txt new file mode 100644 index 000..67b854a --- /dev/null +++ b/Documentation/devicetree/bindings/sound/st,stm32-i2s.txt @@ -0,0 +1,68 @@ +STMicroelectronics STM32 SPI/I2S Controller + +The SPI/I2S block supports I2S/PCM protocols when configured on I2S mode. +Only some SPI instances support I2S. + +Required properties: + - compatible: Must be "st,stm32h7-i2s" + - reg: Offset and length of the device's register set. + - interrupts: Must contain the interrupt line id. + - clocks: Must contain phandle and clock specifier pairs for each entry + in clock-names. + - clock-names: Must contain "i2sclk", "pclk", "x8k" and "x11k". + "i2sclk": clock which feeds the internal clock generator + "pclk": clock which feeds the peripheral bus interface + "x8k": I2S parent clock for sampling rates multiple of 8kHz. + "x11k": I2S parent clock for sampling rates multiple of 11.025kHz. + - dmas: DMA specifiers for tx and rx dma. +See Documentation/devicetree/bindings/dma/stm32-dma.txt. + - dma-names: Identifier for each DMA request line. Must be "tx" and "rx". + - pinctrl-names: should contain only value "default" + - pinctrl-0: see Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt + +Optional properties: + - resets: Reference to a reset controller asserting the reset controller + +The device node should contain one 'port' child node with one child 'endpoint' +node, according to the bindings defined in Documentation/devicetree/bindings/ +graph.txt. + +Example: +sound_card { + compatible = "audio-graph-card"; + dais = <&i2s2_port 0>; +}; + +i2s2: audio-controller@40003800 { + compatible = "st,stm32h7-i2s"; + #sound-dai-cells = <0>; + reg = <0x40003800 0x400>; + interrupts = <36>; + clocks = <&rcc PCLK1>, <&rcc SPI2_CK>, <&rcc PLL1_Q>, <&rcc PLL2_P>; + clock-names = "pclk", "i2sclk", "x8k", "x11k"; + dmas = <&dmamux2 2 39 0x400 0x1>, + <&dmamux2 3 40 0x400 0x1>; + dma-names = "rx", "tx"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2s2>; + + i2s2_port: port@0 { + #address-cells = <1>; + #size-cells = <0>; + + cpu_endpoint: endpoint { + remote-endpoint = <&codec_endpoint>; + format = "i2s"; + bitclock-master = <&codec_endpoint>; + frame-master = <&codec_endpoint>; + }; + }; +}; + +audio-codec { + codec_port: port@0 { + codec_endpoint: endpoint { + remote-endpoint = <&cpu_endpoint>; + }; + }; +}; -- 1.9.1
[PATCH 1/2] ASoC: stm32: spdifrx: fix typo in function name.
Fix function name prefix for naming consistency. Signed-off-by: olivier moysan --- sound/soc/stm/stm32_spdifrx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index b9bdefc..42ad2ae 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -858,8 +858,8 @@ static void stm32_spdifrx_shutdown(struct snd_pcm_substream *substream, {} }; -static int stm_spdifrx_parse_of(struct platform_device *pdev, - struct stm32_spdifrx_data *spdifrx) +static int stm32_spdifrx_parse_of(struct platform_device *pdev, + struct stm32_spdifrx_data *spdifrx) { struct device_node *np = pdev->dev.of_node; const struct of_device_id *of_id; @@ -914,7 +914,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) platform_set_drvdata(pdev, spdifrx); - ret = stm_spdifrx_parse_of(pdev, spdifrx); + ret = stm32_spdifrx_parse_of(pdev, spdifrx); if (ret) return ret; -- 1.9.1
[PATCH 0/2] ASoC: stm32: spdifrx: change dai name
This patchset changes spdifrx dai name and fixes a typo issue. Olivier Moysan (2): ASoC: stm32: spdifrx: fix typo in function name. ASoC: stm32: spdifrx: Use default dai name sound/soc/stm/stm32_spdifrx.c | 7 +++ 1 file changed, 3 insertions(+), 4 deletions(-) -- 1.9.1
[PATCH 2/2] ASoC: stm32: spdifrx: Use default dai name
Use dai name provided by framework from dev_name() function. Signed-off-by: olivier moysan --- sound/soc/stm/stm32_spdifrx.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index 42ad2ae..373df4f 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -819,7 +819,6 @@ static void stm32_spdifrx_shutdown(struct snd_pcm_substream *substream, static struct snd_soc_dai_driver stm32_spdifrx_dai[] = { { - .name = "spdifrx-capture-cpu-dai", .probe = stm32_spdifrx_dai_probe, .capture = { .stream_name = "CPU-Capture", -- 1.9.1
[PATCH 1/1] ASoC: stm32: add of dependency for stm32 drivers
Add of dependency for STM32 ASoC drivers. DFSDM of dependency is already inherited from STM32_DFSDM_ADC dependency. Signed-off-by: olivier moysan --- sound/soc/stm/Kconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig index 3ad881f..b5375f9 100644 --- a/sound/soc/stm/Kconfig +++ b/sound/soc/stm/Kconfig @@ -3,6 +3,7 @@ menu "STMicroelectronics STM32 SOC audio support" config SND_SOC_STM32_SAI tristate "STM32 SAI interface (Serial Audio Interface) support" depends on ARCH_STM32 || COMPILE_TEST + depends on OF depends on SND_SOC select SND_SOC_GENERIC_DMAENGINE_PCM select REGMAP_MMIO @@ -12,6 +13,7 @@ config SND_SOC_STM32_SAI config SND_SOC_STM32_I2S tristate "STM32 I2S interface (SPI/I2S block) support" depends on ARCH_STM32 || COMPILE_TEST + depends on OF depends on SND_SOC select SND_SOC_GENERIC_DMAENGINE_PCM select REGMAP_MMIO @@ -21,6 +23,7 @@ config SND_SOC_STM32_I2S config SND_SOC_STM32_SPDIFRX tristate "STM32 S/PDIF receiver (SPDIFRX) support" depends on ARCH_STM32 || COMPILE_TEST + depends on OF depends on SND_SOC select SND_SOC_GENERIC_DMAENGINE_PCM select REGMAP_MMIO -- 1.9.1
Re: [alsa-devel] [PATCH 1/1] ASoC: stm32: add of dependency for stm32 drivers
hello, On 02/01/2018 10:10 AM, Ladislav Michl wrote: > On Thu, Feb 01, 2018 at 09:54:41AM +0100, Olivier Moysan wrote: >> Add of dependency for STM32 ASoC drivers. >> DFSDM of dependency is already inherited >> from STM32_DFSDM_ADC dependency. >> >> Signed-off-by: olivier moysan >> --- >> sound/soc/stm/Kconfig | 3 +++ >> 1 file changed, 3 insertions(+) >> >> diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig >> index 3ad881f..b5375f9 100644 >> --- a/sound/soc/stm/Kconfig >> +++ b/sound/soc/stm/Kconfig >> @@ -3,6 +3,7 @@ menu "STMicroelectronics STM32 SOC audio support" >> config SND_SOC_STM32_SAI >> tristate "STM32 SAI interface (Serial Audio Interface) support" >> depends on ARCH_STM32 || COMPILE_TEST >> +depends on OF > > depends on (ARCH_STM32 && OF) || COMPILE_TEST ? > I can find in many configs "depends on OF && (ARCH_X || COMPILE_TEST)" This seems reasonable to me, as the driver always requires OF dependency, regardless the compilation context. In fact, I cannot see why OF would not have to be selected for COMPILE_TEST. BRs >> depends on SND_SOC >> select SND_SOC_GENERIC_DMAENGINE_PCM >> select REGMAP_MMIO >> @@ -12,6 +13,7 @@ config SND_SOC_STM32_SAI >> config SND_SOC_STM32_I2S >> tristate "STM32 I2S interface (SPI/I2S block) support" >> depends on ARCH_STM32 || COMPILE_TEST >> +depends on OF >> depends on SND_SOC >> select SND_SOC_GENERIC_DMAENGINE_PCM >> select REGMAP_MMIO >> @@ -21,6 +23,7 @@ config SND_SOC_STM32_I2S >> config SND_SOC_STM32_SPDIFRX >> tristate "STM32 S/PDIF receiver (SPDIFRX) support" >> depends on ARCH_STM32 || COMPILE_TEST >> +depends on OF >> depends on SND_SOC >> select SND_SOC_GENERIC_DMAENGINE_PCM >> select REGMAP_MMIO >> -- >> 1.9.1 >> >> ___ >> Alsa-devel mailing list >> alsa-de...@alsa-project.org >> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
[PATCH 0/3] ASoC: stm32: sai: add support of iec958 controls
This patchset adds support of iec958 controls to STM32 SAI driver. The patch makes use of iec958 control status helpers previously proposed and discussed through the following threads: https://patchwork.kernel.org/patch/8062601/ https://patchwork.kernel.org/patch/8091551/ (v2) https://patchwork.kernel.org/patch/8462961/ (v3) https://patchwork.kernel.org/patch/8533731/ (v4) Olivier Moysan (3): ALSA: pcm: add IEC958 channel status control helper ASoC: stm32: sai: add iec958 controls support ASoC: dmaengine_pcm: document process callback include/sound/dmaengine_pcm.h | 2 + include/sound/pcm_iec958.h| 19 +++ sound/core/pcm_iec958.c | 114 ++ sound/soc/stm/Kconfig | 1 + sound/soc/stm/stm32_sai_sub.c | 101 - 5 files changed, 225 insertions(+), 12 deletions(-) -- 1.9.1
[PATCH 2/3] ASoC: stm32: sai: add iec958 controls support
Add support of iec958 controls for STM32 SAI. Signed-off-by: Olivier Moysan --- sound/core/pcm_iec958.c | 1 + sound/soc/stm/Kconfig | 1 + sound/soc/stm/stm32_sai_sub.c | 101 +- 3 files changed, 91 insertions(+), 12 deletions(-) diff --git a/sound/core/pcm_iec958.c b/sound/core/pcm_iec958.c index aba1f522e98a..c34735ac3c48 100644 --- a/sound/core/pcm_iec958.c +++ b/sound/core/pcm_iec958.c @@ -19,6 +19,7 @@ static int snd_pcm_iec958_info(struct snd_kcontrol *kcontrol, { uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; uinfo->count = 1; + return 0; } diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig index 48f9ddd94016..9b2681397dba 100644 --- a/sound/soc/stm/Kconfig +++ b/sound/soc/stm/Kconfig @@ -6,6 +6,7 @@ config SND_SOC_STM32_SAI depends on SND_SOC select SND_SOC_GENERIC_DMAENGINE_PCM select REGMAP_MMIO + select SND_PCM_IEC958 help Say Y if you want to enable SAI for STM32 diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index cfeb219e1d78..c2e487e133aa 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "stm32_sai.h" @@ -96,7 +97,7 @@ * @slot_mask: rx or tx active slots mask. set at init or at runtime * @data_size: PCM data width. corresponds to PCM substream width. * @spdif_frm_cnt: S/PDIF playback frame counter - * @spdif_status_bits: S/PDIF status bits + * @snd_aes_iec958: iec958 data */ struct stm32_sai_sub_data { struct platform_device *pdev; @@ -125,7 +126,7 @@ struct stm32_sai_sub_data { int slot_mask; int data_size; unsigned int spdif_frm_cnt; - unsigned char spdif_status_bits[SAI_IEC60958_STATUS_BYTES]; + struct snd_aes_iec958 iec958; }; enum stm32_sai_fifo_th { @@ -184,10 +185,6 @@ static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg) } } -static const unsigned char default_status_bits[SAI_IEC60958_STATUS_BYTES] = { - 0, 0, 0, IEC958_AES3_CON_FS_48000, -}; - static const struct regmap_config stm32_sai_sub_regmap_config_f4 = { .reg_bits = 32, .reg_stride = 4, @@ -619,6 +616,59 @@ static void stm32_sai_set_frame(struct snd_soc_dai *cpu_dai) } } +static void stm32_sai_set_channel_status(struct stm32_sai_sub_data *sai, +struct snd_pcm_runtime *runtime) +{ + if (!runtime) + return; + + /* Force the sample rate according to runtime rate */ + switch (runtime->rate) { + case 22050: + sai->iec958.status[3] = IEC958_AES3_CON_FS_22050; + break; + case 44100: + sai->iec958.status[3] = IEC958_AES3_CON_FS_44100; + break; + case 88200: + sai->iec958.status[3] = IEC958_AES3_CON_FS_88200; + break; + case 176400: + sai->iec958.status[3] = IEC958_AES3_CON_FS_176400; + break; + case 24000: + sai->iec958.status[3] = IEC958_AES3_CON_FS_24000; + break; + case 48000: + sai->iec958.status[3] = IEC958_AES3_CON_FS_48000; + break; + case 96000: + sai->iec958.status[3] = IEC958_AES3_CON_FS_96000; + break; + case 192000: + sai->iec958.status[3] = IEC958_AES3_CON_FS_192000; + break; + case 32000: + sai->iec958.status[3] = IEC958_AES3_CON_FS_32000; + break; + default: + sai->iec958.status[3] = IEC958_AES3_CON_FS_NOTID; + break; + } +} + +static int stm32_sai_iec958_set(struct snd_pcm_iec958_params *iec_param) +{ + struct stm32_sai_sub_data *sai = iec_param->private_data; + + if (!sai->substream) + return 0; + + stm32_sai_set_channel_status(sai, sai->substream->runtime); + + return 0; +} + static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, struct snd_pcm_hw_params *params) { @@ -709,7 +759,11 @@ static int stm32_sai_hw_params(struct snd_pcm_substream *substream, sai->data_size = params_width(params); - if (!STM_SAI_PROTOCOL_IS_SPDIF(sai)) { + if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { + /* Rate not already set in runtime structure */ + substream->runtime->rate = params_rate(params); + stm32_sai_set_channel_status(sai, substream->runtime); + } else { ret = stm32_sai_set_slots(cpu_dai); if (ret < 0) return ret; @@ -789,6 +843,28 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream, sai->subst
[PATCH 1/3] ALSA: pcm: add IEC958 channel status control helper
From: Arnaud Pouliquen Add IEC958 channel status helper that creates control to handle the IEC60958 status bits. Signed-off-by: Arnaud Pouliquen Signed-off-by: Olivier Moysan --- include/sound/pcm_iec958.h | 19 sound/core/pcm_iec958.c| 113 + 2 files changed, 132 insertions(+) diff --git a/include/sound/pcm_iec958.h b/include/sound/pcm_iec958.h index 0939aa45e2fe..3c9701a9b1b0 100644 --- a/include/sound/pcm_iec958.h +++ b/include/sound/pcm_iec958.h @@ -4,9 +4,28 @@ #include +/** + * struct snd_pcm_iec958_params: IEC 60958 controls parameters + * @ctrl_set: control set callback + * This callback is optional and shall be used to set associated driver + * configuration. + * @iec: Mandatory pointer to iec958 structure. + * @cs: Mandatory pointer to AES/IEC958 channel status bits. + * @cs_len: size in byte of the AES/IEC958 channel status bits. + * @private_data: Optional private pointer to driver context. + */ +struct snd_pcm_iec958_params { + int (*ctrl_set)(struct snd_pcm_iec958_params *iec_param); + unsigned char *cs; + unsigned char cs_len; + void *private_data; +}; + int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs, size_t len); int snd_pcm_create_iec958_consumer_hw_params(struct snd_pcm_hw_params *params, u8 *cs, size_t len); +int snd_pcm_add_iec958_ctl(struct snd_pcm *pcm, int subdevice, int stream, + struct snd_pcm_iec958_params *params); #endif diff --git a/sound/core/pcm_iec958.c b/sound/core/pcm_iec958.c index 5e6aed64f451..aba1f522e98a 100644 --- a/sound/core/pcm_iec958.c +++ b/sound/core/pcm_iec958.c @@ -7,11 +7,88 @@ */ #include #include +#include #include +#include #include #include #include +static int snd_pcm_iec958_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +/* + * IEC958 channel status default controls callbacks + */ +static int snd_pcm_iec958_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uctl) +{ + struct snd_pcm_iec958_params *params = snd_kcontrol_chip(kcontrol); + int i; + + for (i = 0; i < params->cs_len; i++) + uctl->value.iec958.status[i] = params->cs[i]; + + return 0; +} + +static int snd_pcm_iec958_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uctl) +{ + struct snd_pcm_iec958_params *params = snd_kcontrol_chip(kcontrol); + int err = 0; + unsigned int i, updated = 0; + unsigned char old_status[5]; + + for (i = 0; i < params->cs_len; i++) { + if (params->cs[i] != uctl->value.iec958.status[i]) + updated = 1; + } + + if (!updated) + return 0; + + /* Store current status to restore them in error case */ + for (i = 0; i < params->cs_len; i++) { + old_status[i] = params->cs[i]; + params->cs[i] = uctl->value.iec958.status[i]; + } + + if (params->ctrl_set) + err = params->ctrl_set(params); + if (err < 0) { + for (i = 0; i < params->cs_len; i++) + params->cs[i] = old_status[i]; + } + + return err; +} + +static const struct snd_kcontrol_new iec958_ctls[] = { + { + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_VOLATILE), + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = snd_pcm_iec958_info, + .get = snd_pcm_iec958_get, + .put = snd_pcm_iec958_put, + }, + { + .access = (SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE), + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT), + .info = snd_pcm_iec958_info, + .get = snd_pcm_iec958_get, + }, +}; + static int create_iec958_consumer(uint rate, uint sample_width, u8 *cs, size_t len) { @@ -21,6 +98,9 @@ static int create_iec958_consumer(uint rate, uint sample_width, return -EINVAL; switch (rate) { + case 0: + fs = IEC958_AES3_CON_FS_NOTID; + break; case 32000: fs = IEC958_AES3_CON_FS_32000; break; @@ -48,6 +128,9 @@ static int create_iec958_consumer(uint rate, uint sample_width, if (len > 4) { switch (sample_width) { + case 0: +
[PATCH 3/3] ASoC: dmaengine_pcm: document process callback
Add missing description of process callback. Fixes: 78648092ef46 ("ASoC: dmaengine_pcm: add processing support") Signed-off-by: Olivier Moysan --- include/sound/dmaengine_pcm.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index 47ef486852ed..e3481eebdd98 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -118,6 +118,8 @@ void snd_dmaengine_pcm_set_config_from_dai_data( * PCM substream. Will be called from the PCM drivers hwparams callback. * @compat_request_channel: Callback to request a DMA channel for platforms * which do not use devicetree. + * @process: Callback used to apply processing on samples transferred from/to + * user space. * @compat_filter_fn: Will be used as the filter function when requesting a * channel for platforms which do not use devicetree. The filter parameter * will be the DAI's DMA data. -- 1.9.1
Re: [PATCH 0/3] ASoC: stm32: sai: add support of iec958 controls
Hello Takashi, On 04/17/2018 01:17 PM, Mark Brown wrote: > On Tue, Apr 17, 2018 at 08:29:17AM +0000, Olivier MOYSAN wrote: > >> I guess the blocking patch in this patchset is the patch "add IEC958 >> channel status control helper". This patch has been reviewed several >> times, but did not get a ack so far. >> If you think these helpers will not be merged, I will reintegrate the >> corresponding code in stm driver. >> Please let me know, if I need to prepare a v2 without helpers, or if we >> can go further in the review of iec helpers patch ? > > I don't mind either way but you're right here, I'm waiting for Takashi > to review the first patch. I'd probably be OK with it just integrated > into the driver if we have to go that way though. > Kind reminder. Would you have some comments on this patchset ? Thanks olivier
[PATCH 2/4] ASoC: dt-bindings: add mclk support to cs42l51
Add clocks properties to cs42l51 Cirrus codec, to support master clock provider. Signed-off-by: Olivier Moysan --- Documentation/devicetree/bindings/sound/cs42l51.txt | 17 + Documentation/devicetree/bindings/trivial-devices.txt | 1 - 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/sound/cs42l51.txt diff --git a/Documentation/devicetree/bindings/sound/cs42l51.txt b/Documentation/devicetree/bindings/sound/cs42l51.txt new file mode 100644 index ..4b5de33ce377 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cs42l51.txt @@ -0,0 +1,17 @@ +CS42L51 audio CODEC + +Optional properties: + + - clocks : a list of phandles + clock-specifiers, one for each entry in +clock-names + + - clock-names : must contain "MCLK" + +Example: + +cs42l51: cs42l51@4a { + compatible = "cirrus,cs42l51"; + reg = <0x4a>; + clocks = <&mclk_prov>; + clock-names = "MCLK"; +}; diff --git a/Documentation/devicetree/bindings/trivial-devices.txt b/Documentation/devicetree/bindings/trivial-devices.txt index 763a2808a95c..69c934aec13b 100644 --- a/Documentation/devicetree/bindings/trivial-devices.txt +++ b/Documentation/devicetree/bindings/trivial-devices.txt @@ -35,7 +35,6 @@ at,24c08 i2c serial eeprom (24cxx) atmel,at97sc3204t i2c trusted platform module (TPM) capella,cm32181CM32181: Ambient Light Sensor capella,cm3232 CM3232: Ambient Light Sensor -cirrus,cs42l51 Cirrus Logic CS42L51 audio codec dallas,ds1374 I2C, 32-Bit Binary Counter Watchdog RTC with Trickle Charger and Reset Input/Output dallas,ds1631 High-Precision Digital Thermometer dallas,ds1672 Dallas DS1672 Real-time Clock -- 2.7.4
[PATCH 1/4] ASoC: dt-bindings: add mclk provider support to stm32 sai
add mclk provider support to stm32 sai Signed-off-by: Olivier Moysan --- Documentation/devicetree/bindings/sound/st,stm32-sai.txt | 7 +++ 1 file changed, 7 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/st,stm32-sai.txt b/Documentation/devicetree/bindings/sound/st,stm32-sai.txt index 3a3fc506e43a..3f4467ff0aa2 100644 --- a/Documentation/devicetree/bindings/sound/st,stm32-sai.txt +++ b/Documentation/devicetree/bindings/sound/st,stm32-sai.txt @@ -31,7 +31,11 @@ SAI subnodes required properties: - reg: Base address and size of SAI sub-block register set. - clocks: Must contain one phandle and clock specifier pair for sai_ck which feeds the internal clock generator. + If the SAI shares a master clock, with another SAI set as MCLK + clock provider, SAI provider phandle must be specified here. - clock-names: Must contain "sai_ck". + Must also contain "MCLK", if SAI shares a master clock, + with a SAI set as MCLK clock provider. - dmas: see Documentation/devicetree/bindings/dma/stm32-dma.txt - dma-names: identifier string for each DMA request line "tx": if sai sub-block is configured as playback DAI @@ -51,6 +55,9 @@ SAI subnodes Optional properties: configured according to protocol defined in related DAI link node, such as i2s, left justified, right justified, dsp and pdm protocols. Note: ac97 protocol is not supported by SAI driver + - #clock-cells: should be 0. This property must be present if the SAI device + is a master clock provider, according to clocks bindings, described in + Documentation/devicetree/bindings/clock/clock-bindings.txt. The device node should contain one 'port' child node with one child 'endpoint' node, according to the bindings defined in Documentation/devicetree/bindings/ -- 2.7.4
[PATCH 3/4] ASoC: stm32: sai: set sai as mclk clock provider
Add master clock generation support in STM32 SAI. The master clock provided by SAI can be used to feed a codec. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_sai.h | 3 + sound/soc/stm/stm32_sai_sub.c | 275 -- 2 files changed, 242 insertions(+), 36 deletions(-) diff --git a/sound/soc/stm/stm32_sai.h b/sound/soc/stm/stm32_sai.h index f25422174909..08de899c766b 100644 --- a/sound/soc/stm/stm32_sai.h +++ b/sound/soc/stm/stm32_sai.h @@ -91,6 +91,9 @@ #define SAI_XCR1_OSR_SHIFT 26 #define SAI_XCR1_OSR BIT(SAI_XCR1_OSR_SHIFT) +#define SAI_XCR1_MCKEN_SHIFT 27 +#define SAI_XCR1_MCKEN BIT(SAI_XCR1_MCKEN_SHIFT) + /*** Bit definition for SAI_XCR2 register ***/ #define SAI_XCR2_FTH_SHIFT 0 #define SAI_XCR2_FTH_MASK GENMASK(2, SAI_XCR2_FTH_SHIFT) diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 56a227e0bd71..31d22abd3204 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -68,6 +69,8 @@ #define SAI_IEC60958_BLOCK_FRAMES 192 #define SAI_IEC60958_STATUS_BYTES 24 +#define SAI_MCLK_NAME_LEN 32 + /** * struct stm32_sai_sub_data - private data of SAI sub block (block A or B) * @pdev: device data pointer @@ -80,6 +83,7 @@ * @pdata: SAI block parent data pointer * @np_sync_provider: synchronization provider node * @sai_ck: kernel clock feeding the SAI clock generator + * @sai_mclk: master clock from SAI mclk provider * @phys_addr: SAI registers physical base address * @mclk_rate: SAI block master clock frequency (Hz). set at init * @id: SAI sub block id corresponding to sub-block A or B @@ -110,6 +114,7 @@ struct stm32_sai_sub_data { struct stm32_sai_data *pdata; struct device_node *np_sync_provider; struct clk *sai_ck; + struct clk *sai_mclk; dma_addr_t phys_addr; unsigned int mclk_rate; unsigned int id; @@ -251,6 +256,177 @@ static const struct snd_kcontrol_new iec958_ctls = { .put = snd_pcm_iec958_put, }; +struct stm32_sai_mclk_data { + struct clk_hw hw; + unsigned long freq; + struct stm32_sai_sub_data *sai_data; +}; + +#define to_mclk_data(_hw) container_of(_hw, struct stm32_sai_mclk_data, hw) +#define STM32_SAI_MAX_CLKS 1 + +static int stm32_sai_get_clk_div(struct stm32_sai_sub_data *sai, +unsigned long input_rate, +unsigned long output_rate) +{ + int version = sai->pdata->conf->version; + int div; + + div = DIV_ROUND_CLOSEST(input_rate, output_rate); + if (div > SAI_XCR1_MCKDIV_MAX(version)) { + dev_err(&sai->pdev->dev, "Divider %d out of range\n", div); + return -EINVAL; + } + dev_dbg(&sai->pdev->dev, "SAI divider %d\n", div); + + if (input_rate % div) + dev_dbg(&sai->pdev->dev, + "Rate not accurate. requested (%ld), actual (%ld)\n", + output_rate, input_rate / div); + + return div; +} + +static int stm32_sai_set_clk_div(struct stm32_sai_sub_data *sai, +unsigned int div) +{ + int version = sai->pdata->conf->version; + int ret, cr1, mask; + + if (div > SAI_XCR1_MCKDIV_MAX(version)) { + dev_err(&sai->pdev->dev, "Divider %d out of range\n", div); + return -EINVAL; + } + + mask = SAI_XCR1_MCKDIV_MASK(SAI_XCR1_MCKDIV_WIDTH(version)); + cr1 = SAI_XCR1_MCKDIV_SET(div); + ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1); + if (ret < 0) + dev_err(&sai->pdev->dev, "Failed to update CR1 register\n"); + + return ret; +} + +static long stm32_sai_mclk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); + struct stm32_sai_sub_data *sai = mclk->sai_data; + int div; + + div = stm32_sai_get_clk_div(sai, *prate, rate); + if (div < 0) + return div; + + mclk->freq = *prate / div; + + return mclk->freq; +} + +static unsigned long stm32_sai_mclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); + + return mclk->freq; +} + +static int stm32_sai_mclk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); + struct stm32_sai_sub_data *sa
[PATCH 0/4] ASoC: stm32: sai: add mclk clock provider
This patch set allows to use STM32 SAI interface as a master clock provider. The Cirrus cs42l51 codec patch gives the example of the source code required on codec side, to use SAI mclk provider. These patches allow to cover the following use cases: - SAI is master or slave and is master clock provider - SAI is master or slave and is not master clock provider (another sai provides the mclk clock) Implementation overview: - mclk configuration SAI is registered in clock framework: - either as a mclk provider (optional in DT). SAI generates mclk when requested by DAPM - or as a mclk consumer (optional in DT). cs42l51 codec is configured as a mclk consumer (optional in DT) - mclk power A DAPM is registered in codec to handle mclk enable/disable (DAPM route is set in soundcard node) - mclk rate: mclk rate is computed at runtime from stream rate and mclk-fs ratio provided in DT mclk rate is propagated through sysclk callback to the SAI which is mclk consumer. The mclk rate is set exclusively to avoid concurrent rate requests on SAI master. Regards Olivier Olivier Moysan (4): ASoC: dt-bindings: add mclk provider support to stm32 sai ASoC: dt-bindings: add mclk support to cs42l51 ASoC: stm32: sai: set sai as mclk clock provider ASoC: cs42l51: add mclk support .../devicetree/bindings/sound/cs42l51.txt | 17 ++ .../devicetree/bindings/sound/st,stm32-sai.txt | 7 + .../devicetree/bindings/trivial-devices.txt| 1 - sound/soc/codecs/cs42l51.c | 8 + sound/soc/stm/stm32_sai.h | 3 + sound/soc/stm/stm32_sai_sub.c | 275 ++--- 6 files changed, 274 insertions(+), 37 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/cs42l51.txt -- 2.7.4
[PATCH 4/4] ASoC: cs42l51: add mclk support
Add MCLK dapm to allow configuration of cirrus CS42l51 codec as a master clock consumer. Signed-off-by: Olivier Moysan --- sound/soc/codecs/cs42l51.c | 8 1 file changed, 8 insertions(+) diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index 5080d7a3c279..eb40bff54cec 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -237,6 +237,10 @@ static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = { &cs42l51_adcr_mux_controls), }; +static const struct snd_soc_dapm_widget cs42l51_dapm_mclk_widgets[] = { + SND_SOC_DAPM_CLOCK_SUPPLY("MCLK") +}; + static const struct snd_soc_dapm_route cs42l51_routes[] = { {"HPL", NULL, "Left DAC"}, {"HPR", NULL, "Right DAC"}, @@ -487,6 +491,10 @@ static struct snd_soc_dai_driver cs42l51_dai = { static int cs42l51_component_probe(struct snd_soc_component *component) { int ret, reg; + struct snd_soc_dapm_context *dapm; + + dapm = snd_soc_component_get_dapm(component); + snd_soc_dapm_new_controls(dapm, cs42l51_dapm_mclk_widgets, 1); /* * DAC configuration -- 2.7.4
[PATCH 1/1] ASoC: cs42l51: fix mclk support
Fixes: 5e8d63a726f8 ("ASoC: cs42l51: add mclk support") The MCLK clock is made optional for cs42l51 codec. However, ASoC DAPM clock supply widget, expects the clock to be defined unconditionally. Register MCLK DAPM conditionally in codec driver, depending on clock presence in DT. Signed-off-by: Olivier Moysan --- sound/soc/codecs/cs42l51.c | 15 ++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index eb40bff54cec..fd2bd74024c1 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -21,6 +21,7 @@ * - master mode *NOT* supported */ +#include #include #include #include @@ -41,6 +42,7 @@ enum master_slave_mode { struct cs42l51_private { unsigned int mclk; + struct clk *mclk_handle; unsigned int audio_mode;/* The mode (I2S or left-justified) */ enum master_slave_mode func; }; @@ -492,9 +494,13 @@ static int cs42l51_component_probe(struct snd_soc_component *component) { int ret, reg; struct snd_soc_dapm_context *dapm; + struct cs42l51_private *cs42l51; + cs42l51 = snd_soc_component_get_drvdata(component); dapm = snd_soc_component_get_dapm(component); - snd_soc_dapm_new_controls(dapm, cs42l51_dapm_mclk_widgets, 1); + + if (cs42l51->mclk_handle) + snd_soc_dapm_new_controls(dapm, cs42l51_dapm_mclk_widgets, 1); /* * DAC configuration @@ -548,6 +554,13 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap) dev_set_drvdata(dev, cs42l51); + cs42l51->mclk_handle = devm_clk_get(dev, "MCLK"); + if (IS_ERR(cs42l51->mclk_handle)) { + if (PTR_ERR(cs42l51->mclk_handle) != -ENOENT) + return PTR_ERR(cs42l51->mclk_handle); + cs42l51->mclk_handle = NULL; + } + /* Verify that we have a CS42L51 */ ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val); if (ret < 0) { -- 2.7.4
[PATCH 2/2] ASoC: stm32: sai: fix master clock naming
Fixes: 8307b2afd386 ("ASoC: stm32: sai: set sai as mclk clock provider") Fix warning issued by strncat when bound equals to source length. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_sai_sub.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 31d22abd3204..ea05cc91aa05 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -404,12 +404,11 @@ static int stm32_sai_add_mclk_provider(struct stm32_sai_sub_data *sai) * String after "_" char is stripped in parent name. */ p = mclk_name; - while (*s && *s != '_' && (i < (SAI_MCLK_NAME_LEN - 6))) { + while (*s && *s != '_' && (i < (SAI_MCLK_NAME_LEN - 7))) { *p++ = *s++; i++; } - STM_SAI_IS_SUB_A(sai) ? - strncat(p, "a_mclk", 6) : strncat(p, "b_mclk", 6); + STM_SAI_IS_SUB_A(sai) ? strcat(p, "a_mclk") : strcat(p, "b_mclk"); mclk->hw.init = CLK_HW_INIT(mclk_name, pname, &mclk_ops, 0); mclk->sai_data = sai; -- 2.7.4
[PATCH 1/2] ASoC: stm32: add clock dependency for sai
Fixes: 8307b2afd386 ("ASoC: stm32: sai: set sai as mclk clock provider") Add COMMON_CLK dependency for STM32 SAI, as it is required by clock provider. Signed-off-by: Olivier Moysan --- sound/soc/stm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig index 9b2681397dba..c66ffa72057e 100644 --- a/sound/soc/stm/Kconfig +++ b/sound/soc/stm/Kconfig @@ -3,6 +3,7 @@ menu "STMicroelectronics STM32 SOC audio support" config SND_SOC_STM32_SAI tristate "STM32 SAI interface (Serial Audio Interface) support" depends on (ARCH_STM32 && OF) || COMPILE_TEST + depends on COMMON_CLK depends on SND_SOC select SND_SOC_GENERIC_DMAENGINE_PCM select REGMAP_MMIO -- 2.7.4
[PATCH 0/2] ASoC: stm32: sai: fix kbuild errors
These patches fix following compilation errors: - >> sound/soc/stm/stm32_sai_sub.c:260:16: error: field 'hw' has incomplete type - >> sound/soc/stm/stm32_sai_sub.c:412:3: warning: 'strncat' specified bound 6 equals source length [-Wstringop-overflow=] Olivier Moysan (2): ASoC: stm32: add clock dependency for sai ASoC: stm32: sai: fix master clock naming sound/soc/stm/Kconfig | 1 + sound/soc/stm/stm32_sai_sub.c | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) -- 2.7.4
[PATCH 1/5] ASoC: stm32: sai: fix iec958 controls indexation
Allow indexation of sai iec958 controls according to device id. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_sai_sub.c | 7 --- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index f9297228c41c..506360b7bc01 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -100,7 +100,7 @@ * @slot_mask: rx or tx active slots mask. set at init or at runtime * @data_size: PCM data width. corresponds to PCM substream width. * @spdif_frm_cnt: S/PDIF playback frame counter - * @snd_aes_iec958: iec958 data + * @iec958: iec958 data * @ctrl_lock: control lock */ struct stm32_sai_sub_data { @@ -1068,11 +1068,12 @@ static int stm32_sai_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *cpu_dai) { struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); + struct snd_kcontrol_new knew = iec958_ctls; if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { dev_dbg(&sai->pdev->dev, "%s: register iec controls", __func__); - return snd_ctl_add(rtd->pcm->card, - snd_ctl_new1(&iec958_ctls, sai)); + knew.device = rtd->pcm->device; + return snd_ctl_add(rtd->pcm->card, snd_ctl_new1(&knew, sai)); } return 0; -- 2.7.4
[PATCH 3/5] ASoC: stm32: sai: fix race condition in irq handler
When snd_pcm_stop_xrun() is called in interrupt routine, substream context may have already been released. Add protection on substream context. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_sai_sub.c | 13 - 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index e418f446e03b..cad415e03b5e 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -102,6 +102,7 @@ * @spdif_frm_cnt: S/PDIF playback frame counter * @iec958: iec958 data * @ctrl_lock: control lock + * @irq_lock: prevent race condition with IRQ */ struct stm32_sai_sub_data { struct platform_device *pdev; @@ -133,6 +134,7 @@ struct stm32_sai_sub_data { unsigned int spdif_frm_cnt; struct snd_aes_iec958 iec958; struct mutex ctrl_lock; /* protect resources accessed by controls */ + spinlock_t irq_lock; /* used to prevent race condition with IRQ */ }; enum stm32_sai_fifo_th { @@ -474,8 +476,10 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid) status = SNDRV_PCM_STATE_XRUN; } - if (status != SNDRV_PCM_STATE_RUNNING) + spin_lock(&sai->irq_lock); + if (status != SNDRV_PCM_STATE_RUNNING && sai->substream) snd_pcm_stop_xrun(sai->substream); + spin_unlock(&sai->irq_lock); return IRQ_HANDLED; } @@ -679,8 +683,11 @@ static int stm32_sai_startup(struct snd_pcm_substream *substream, { struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); int imr, cr2, ret; + unsigned long flags; + spin_lock_irqsave(&sai->irq_lock, flags); sai->substream = substream; + spin_unlock_irqrestore(&sai->irq_lock, flags); if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { snd_pcm_hw_constraint_mask64(substream->runtime, @@ -1059,6 +1066,7 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); + unsigned long flags; regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, SAI_XIMR_MASK, 0); @@ -1069,7 +1077,9 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream, clk_rate_exclusive_put(sai->sai_mclk); + spin_lock_irqsave(&sai->irq_lock, flags); sai->substream = NULL; + spin_unlock_irqrestore(&sai->irq_lock, flags); } static int stm32_sai_pcm_new(struct snd_soc_pcm_runtime *rtd, @@ -1433,6 +1443,7 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) sai->pdev = pdev; mutex_init(&sai->ctrl_lock); + spin_lock_init(&sai->irq_lock); platform_set_drvdata(pdev, sai); sai->pdata = dev_get_drvdata(pdev->dev.parent); -- 2.7.4
[PATCH 0/5] ASoC: stm32: sai: miscellaneous fixes
This patch-set is a collection of fixes for STM32 SAI driver. Olivier Moysan (5): ASoC: stm32: sai: fix iec958 controls indexation ASoC: stm32: sai: fix exposed capabilities in spdif mode ASoC: stm32: sai: fix race condition in irq handler ASoC: stm32: sai: fix oversampling mode ASoC: stm32: sai: fix set_sync service sound/soc/stm/stm32_sai.c | 7 --- sound/soc/stm/stm32_sai_sub.c | 48 ++- 2 files changed, 42 insertions(+), 13 deletions(-) -- 2.7.4
[PATCH 2/5] ASoC: stm32: sai: fix exposed capabilities in spdif mode
Change capabilities exposed in SAI S/PDIF mode, to match actually supported formats. In S/PDIF mode only 32 bits stereo is supported. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_sai_sub.c | 8 1 file changed, 8 insertions(+) diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 506360b7bc01..e418f446e03b 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -682,6 +682,14 @@ static int stm32_sai_startup(struct snd_pcm_substream *substream, sai->substream = substream; + if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { + snd_pcm_hw_constraint_mask64(substream->runtime, +SNDRV_PCM_HW_PARAM_FORMAT, +SNDRV_PCM_FMTBIT_S32_LE); + snd_pcm_hw_constraint_single(substream->runtime, +SNDRV_PCM_HW_PARAM_CHANNELS, 2); + } + ret = clk_prepare_enable(sai->sai_ck); if (ret < 0) { dev_err(cpu_dai->dev, "Failed to enable clock: %d\n", ret); -- 2.7.4
[PATCH 5/5] ASoC: stm32: sai: fix set_sync service
Add error check on set_sync function return. Add of_node_put() as of_get_parent() takes a reference which has to be released. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_sai.c | 8 +--- sound/soc/stm/stm32_sai_sub.c | 8 +--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index 14c9591aae42..d68d62f12df5 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -105,6 +105,7 @@ static int stm32_sai_set_sync(struct stm32_sai_data *sai_client, if (!pdev) { dev_err(&sai_client->pdev->dev, "Device not found for node %pOFn\n", np_provider); + of_node_put(np_provider); return -ENODEV; } @@ -113,19 +114,20 @@ static int stm32_sai_set_sync(struct stm32_sai_data *sai_client, dev_err(&sai_client->pdev->dev, "SAI sync provider data not found\n"); ret = -EINVAL; - goto out_put_dev; + goto error; } /* Configure sync client */ ret = stm32_sai_sync_conf_client(sai_client, synci); if (ret < 0) - goto out_put_dev; + goto error; /* Configure sync provider */ ret = stm32_sai_sync_conf_provider(sai_provider, synco); -out_put_dev: +error: put_device(&pdev->dev); + of_node_put(np_provider); return ret; } diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index cb658463ccd1..55d802f51c15 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -1106,7 +1106,7 @@ static int stm32_sai_pcm_new(struct snd_soc_pcm_runtime *rtd, static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) { struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); - int cr1 = 0, cr1_mask; + int cr1 = 0, cr1_mask, ret; sai->cpu_dai = cpu_dai; @@ -1136,8 +1136,10 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) /* Configure synchronization */ if (sai->sync == SAI_SYNC_EXTERNAL) { /* Configure synchro client and provider */ - sai->pdata->set_sync(sai->pdata, sai->np_sync_provider, -sai->synco, sai->synci); + ret = sai->pdata->set_sync(sai->pdata, sai->np_sync_provider, + sai->synco, sai->synci); + if (ret) + return ret; } cr1_mask |= SAI_XCR1_SYNCEN_MASK; -- 2.7.4
[PATCH 4/5] ASoC: stm32: sai: fix oversampling mode
Set OSR bit if mclk/fs ratio is 512. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_sai_sub.c | 12 +--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index cad415e03b5e..cb658463ccd1 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -913,7 +913,7 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, struct snd_pcm_hw_params *params) { struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); - int div = 0; + int div = 0, cr1 = 0; int sai_clk_rate, mclk_ratio, den; unsigned int rate = params_rate(params); @@ -958,13 +958,19 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, } else { if (sai->mclk_rate) { mclk_ratio = sai->mclk_rate / rate; - if ((mclk_ratio != 512) && - (mclk_ratio != 256)) { + if (mclk_ratio == 512) { + cr1 = SAI_XCR1_OSR; + } else if (mclk_ratio != 256) { dev_err(cpu_dai->dev, "Wrong mclk ratio %d\n", mclk_ratio); return -EINVAL; } + + regmap_update_bits(sai->regmap, + STM_SAI_CR1_REGX, + SAI_XCR1_OSR, cr1); + div = stm32_sai_get_clk_div(sai, sai_clk_rate, sai->mclk_rate); if (div < 0) -- 2.7.4
Re: [PATCH] ASoC: stm32: i2s: return the get_irq error
On 4/24/19 5:28 PM, Fabien Dessenne wrote: > During probe, return the "get_irq" error value instead of -ENOENT. This > allows the driver to be deferred probed if needed. > > Signed-off-by: Fabien Dessenne Acked-by: Olivier Moysan > --- > sound/soc/stm/stm32_i2s.c | 5 +++-- > 1 file changed, 3 insertions(+), 2 deletions(-) > > diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c > index 9526342..97d5e99 100644 > --- a/sound/soc/stm/stm32_i2s.c > +++ b/sound/soc/stm/stm32_i2s.c > @@ -838,8 +838,9 @@ static int stm32_i2s_parse_dt(struct platform_device > *pdev, > /* Get irqs */ > irq = platform_get_irq(pdev, 0); > if (irq < 0) { > - dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); > - return -ENOENT; > + if (irq != -EPROBE_DEFER) > + dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); > + return irq; > } > > ret = devm_request_irq(&pdev->dev, irq, stm32_i2s_isr, IRQF_ONESHOT, >
[Linux-stm32][PATCH 2/4] ARM: dts: stm32: add sai pins muxing on stm32mp157
Add SAI pins muxing to stm32mp157. Signed-off-by: Olivier Moysan --- arch/arm/boot/dts/stm32mp157-pinctrl.dtsi | 73 +++ 1 file changed, 73 insertions(+) diff --git a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi index 85c417d9983b..6ea9b9ff45e7 100644 --- a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi +++ b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi @@ -506,6 +506,79 @@ }; }; + sai2a_pins_a: sai2a-0 { + pins { + pinmux = , /* SAI2_SCK_A */ +, /* SAI2_SD_A */ +, /* SAI2_FS_A */ +; /* SAI2_MCLK_A */ + slew-rate = <0>; + drive-push-pull; + bias-disable; + }; + }; + + sai2a_sleep_pins_a: sai2a-1 { + pins { + pinmux = , /* SAI2_SCK_A */ +, /* SAI2_SD_A */ +, /* SAI2_FS_A */ +; /* SAI2_MCLK_A */ + }; + }; + + sai2b_pins_a: sai2b-0 { + pins1 { + pinmux = , /* SAI2_SCK_B */ +, /* SAI2_FS_B */ +; /* SAI2_MCLK_B */ + slew-rate = <0>; + drive-push-pull; + bias-disable; + }; + pins2 { + pinmux = ; /* SAI2_SD_B */ + bias-disable; + }; + }; + + sai2b_sleep_pins_a: sai2b-1 { + pins { + pinmux = , /* SAI2_SD_B */ +, /* SAI2_SCK_B */ +, /* SAI2_FS_B */ +; /* SAI2_MCLK_B */ + }; + }; + + sai2b_pins_b: sai2b-2 { + pins { + pinmux = ; /* SAI2_SD_B */ + bias-disable; + }; + }; + + sai2b_sleep_pins_b: sai2b-3 { + pins { + pinmux = ; /* SAI2_SD_B */ + }; + }; + + sai4a_pins_a: sai4a-0 { + pins { + pinmux = ; /* SAI4_SD_A */ + slew-rate = <0>; + drive-push-pull; + bias-disable; + }; + }; + + sai4a_sleep_pins_a: sai4a-1 { + pins { + pinmux = ; /* SAI4_SD_A */ + }; + }; + sdmmc1_b4_pins_a: sdmmc1-b4-0 { pins { pinmux = , /* SDMMC1_D0 */ -- 2.7.4
[Linux-stm32][PATCH 3/4] ARM: dts: stm32: add i2s support on stm32mp157c
This patch adds support of STM32 I2S on stm32mp157c. Signed-off-by: Olivier Moysan --- arch/arm/boot/dts/stm32mp157c.dtsi | 33 + 1 file changed, 33 insertions(+) diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi index 9ae40240f635..ec2d6475caa2 100644 --- a/arch/arm/boot/dts/stm32mp157c.dtsi +++ b/arch/arm/boot/dts/stm32mp157c.dtsi @@ -365,6 +365,17 @@ status = "disabled"; }; + i2s2: audio-controller@4000b000 { + compatible = "st,stm32h7-i2s"; + #sound-dai-cells = <0>; + reg = <0x4000b000 0x400>; + interrupts = ; + dmas = <&dmamux1 39 0x400 0x01>, + <&dmamux1 40 0x400 0x01>; + dma-names = "rx", "tx"; + status = "disabled"; + }; + spi3: spi@4000c000 { #address-cells = <1>; #size-cells = <0>; @@ -379,6 +390,17 @@ status = "disabled"; }; + i2s3: audio-controller@4000c000 { + compatible = "st,stm32h7-i2s"; + #sound-dai-cells = <0>; + reg = <0x4000c000 0x400>; + interrupts = ; + dmas = <&dmamux1 61 0x400 0x01>, + <&dmamux1 62 0x400 0x01>; + dma-names = "rx", "tx"; + status = "disabled"; + }; + spdifrx: audio-controller@4000d000 { compatible = "st,stm32h7-spdifrx"; #sound-dai-cells = <0>; @@ -607,6 +629,17 @@ status = "disabled"; }; + i2s1: audio-controller@44004000 { + compatible = "st,stm32h7-i2s"; + #sound-dai-cells = <0>; + reg = <0x44004000 0x400>; + interrupts = ; + dmas = <&dmamux1 37 0x400 0x01>, + <&dmamux1 38 0x400 0x01>; + dma-names = "rx", "tx"; + status = "disabled"; + }; + spi4: spi@44005000 { #address-cells = <1>; #size-cells = <0>; -- 2.7.4
[Linux-stm32][PATCH 4/4] ARM: dts: stm32: add i2s pins muxing on stm32mp157
Add I2S pins muxing to stm32mp157. Signed-off-by: Olivier Moysan --- arch/arm/boot/dts/stm32mp157-pinctrl.dtsi | 19 +++ 1 file changed, 19 insertions(+) diff --git a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi index 6ea9b9ff45e7..e29bf7a2c18a 100644 --- a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi +++ b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi @@ -276,6 +276,25 @@ }; }; + i2s2_pins_a: i2s2-0 { + pins { + pinmux = , /* I2S2_SDO */ +, /* I2S2_WS */ +; /* I2S2_CK */ + slew-rate = <1>; + drive-push-pull; + bias-disable; + }; + }; + + i2s2_pins_sleep_a: i2s2-1 { + pins { + pinmux = , /* I2S2_SDO */ +, /* I2S2_WS */ +; /* I2S2_CK */ + }; + }; + ltdc_pins_a: ltdc-a-0 { pins { pinmux = , /* LCD_CLK */ -- 2.7.4
[Linux-stm32][PATCH 1/4] ARM: dts: stm32: add sai support on stm32mp157c
This patch adds support of STM32 SAI on stm32mp157c. Signed-off-by: Olivier Moysan --- arch/arm/boot/dts/stm32mp157c.dtsi | 125 + 1 file changed, 125 insertions(+) diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi index 2afeee65c3ea..9ae40240f635 100644 --- a/arch/arm/boot/dts/stm32mp157c.dtsi +++ b/arch/arm/boot/dts/stm32mp157c.dtsi @@ -708,6 +708,100 @@ status = "disabled"; }; + sai1: sai@4400a000 { + compatible = "st,stm32h7-sai"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x4400a000 0x400>; + reg = <0x4400a000 0x4>; + interrupts = ; + resets = <&rcc SAI1_R>; + status = "disabled"; + + sai1a: audio-controller@4400a004 { + #sound-dai-cells = <0>; + + compatible = "st,stm32-sai-sub-a"; + reg = <0x4 0x1c>; + clocks = <&rcc SAI1_K>; + clock-names = "sai_ck"; + dmas = <&dmamux1 87 0x400 0x01>; + status = "disabled"; + }; + + sai1b: audio-controller@4400a024 { + #sound-dai-cells = <0>; + compatible = "st,stm32-sai-sub-b"; + reg = <0x24 0x1c>; + clocks = <&rcc SAI1_K>; + clock-names = "sai_ck"; + dmas = <&dmamux1 88 0x400 0x01>; + status = "disabled"; + }; + }; + + sai2: sai@4400b000 { + compatible = "st,stm32h7-sai"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x4400b000 0x400>; + reg = <0x4400b000 0x4>; + interrupts = ; + resets = <&rcc SAI2_R>; + status = "disabled"; + + sai2a: audio-controller@4400b004 { + #sound-dai-cells = <0>; + compatible = "st,stm32-sai-sub-a"; + reg = <0x4 0x1c>; + clocks = <&rcc SAI2_K>; + clock-names = "sai_ck"; + dmas = <&dmamux1 89 0x400 0x01>; + status = "disabled"; + }; + + sai2b: audio-controller@4400b024 { + #sound-dai-cells = <0>; + compatible = "st,stm32-sai-sub-b"; + reg = <0x24 0x1c>; + clocks = <&rcc SAI2_K>; + clock-names = "sai_ck"; + dmas = <&dmamux1 90 0x400 0x01>; + status = "disabled"; + }; + }; + + sai3: sai@4400c000 { + compatible = "st,stm32h7-sai"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x4400c000 0x400>; + reg = <0x4400c000 0x4>; + interrupts = ; + resets = <&rcc SAI3_R>; + status = "disabled"; + + sai3a: audio-controller@4400c004 { + #sound-dai-cells = <0>; + compatible = "st,stm32-sai-sub-a"; + reg = <0x04 0x1c>; + clocks = <&rcc SAI3_K>; + clock-names = "sai_ck"; + dmas = <&dmamux1 113 0x400 0x01>; + status = "disabled"; + }; + + sai3b: audio-controller@4400c024 { + #sound-dai-cells = <0>; + compatible = "st,stm32-sai-sub-b"; + reg = <0x2
[Linux-stm32][PATCH 0/4] ARM: dts: stm32: add i2s and sai support on stm32mp157c
This patchset adds support of STM32 SAI and I2S on stm32mp157c Olivier Moysan (4): ARM: dts: stm32: add sai support on stm32mp157c ARM: dts: stm32: add sai pins muxing on stm32mp157 ARM: dts: stm32: add i2s support on stm32mp157c ARM: dts: stm32: add i2s pins muxing on stm32mp157 arch/arm/boot/dts/stm32mp157-pinctrl.dtsi | 92 + arch/arm/boot/dts/stm32mp157c.dtsi| 158 ++ 2 files changed, 250 insertions(+) -- 2.7.4
[PATCH 2/7] ASoC: cs42l51: add regulator management
Add cs42l51 audio codec power supply management through regulator framework. Signed-off-by: Olivier Moysan --- sound/soc/codecs/cs42l51-i2c.c | 6 ++ sound/soc/codecs/cs42l51.c | 44 +- sound/soc/codecs/cs42l51.h | 1 + 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c index 4b5731a41876..8333dbf18ea2 100644 --- a/sound/soc/codecs/cs42l51-i2c.c +++ b/sound/soc/codecs/cs42l51-i2c.c @@ -35,12 +35,18 @@ static int cs42l51_i2c_probe(struct i2c_client *i2c, return cs42l51_probe(&i2c->dev, devm_regmap_init_i2c(i2c, &config)); } +static int cs42l51_i2c_remove(struct i2c_client *i2c) +{ + return cs42l51_remove(&i2c->dev); +} + static struct i2c_driver cs42l51_i2c_driver = { .driver = { .name = "cs42l51", .of_match_table = cs42l51_of_match, }, .probe = cs42l51_i2c_probe, + .remove = cs42l51_i2c_remove, .id_table = cs42l51_i2c_id, }; diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index 80da3cd73e04..f43eb51d2d8d 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "cs42l51.h" @@ -40,11 +41,19 @@ enum master_slave_mode { MODE_MASTER, }; +static const char * const cs42l51_supply_names[] = { + "VL", + "VD", + "VA", + "VAHP", +}; + struct cs42l51_private { unsigned int mclk; struct clk *mclk_handle; unsigned int audio_mode;/* The mode (I2S or left-justified) */ enum master_slave_mode func; + struct regulator_bulk_data supplies[ARRAY_SIZE(cs42l51_supply_names)]; }; #define CS42L51_FORMATS ( \ @@ -550,7 +559,7 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap) { struct cs42l51_private *cs42l51; unsigned int val; - int ret; + int ret, i; if (IS_ERR(regmap)) return PTR_ERR(regmap); @@ -569,6 +578,23 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap) cs42l51->mclk_handle = NULL; } + for (i = 0; i < ARRAY_SIZE(cs42l51->supplies); i++) + cs42l51->supplies[i].supply = cs42l51_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs42l51->supplies), + cs42l51->supplies); + if (ret != 0) { + dev_err(dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(cs42l51->supplies), + cs42l51->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + /* Verify that we have a CS42L51 */ ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val); if (ret < 0) { @@ -587,11 +613,27 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap) ret = devm_snd_soc_register_component(dev, &soc_component_device_cs42l51, &cs42l51_dai, 1); + if (ret < 0) + goto error; + + return 0; + error: + regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies), + cs42l51->supplies); return ret; } EXPORT_SYMBOL_GPL(cs42l51_probe); +int cs42l51_remove(struct device *dev) +{ + struct cs42l51_private *cs42l51 = dev_get_drvdata(dev); + + return regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies), + cs42l51->supplies); +} +EXPORT_SYMBOL_GPL(cs42l51_remove); + const struct of_device_id cs42l51_of_match[] = { { .compatible = "cirrus,cs42l51", }, { } diff --git a/sound/soc/codecs/cs42l51.h b/sound/soc/codecs/cs42l51.h index 0ca805492ac4..aef0ede82c7b 100644 --- a/sound/soc/codecs/cs42l51.h +++ b/sound/soc/codecs/cs42l51.h @@ -22,6 +22,7 @@ struct device; extern const struct regmap_config cs42l51_regmap; int cs42l51_probe(struct device *dev, struct regmap *regmap); +int cs42l51_remove(struct device *dev); extern const struct of_device_id cs42l51_of_match[]; #define CS42L51_CHIP_ID0x1B -- 2.7.4
[PATCH 0/7] ASoC: cs42l51: add some features
This patchset add support of following features to Cirrus CS42L51 audio codec driver: - reset - regulators - master mode - power management - adc volume control Olivier Moysan (7): ASoC: dt-bindings: update cs42l51 bindings ASoC: cs42l51: add regulator management ASoC: cs42l51: add reset management ASoC: cs42l51: add support of master mode ASoC: cs42l51: change mic bias DAPM ASoC: cs42l51: add power management ASoC: cs42l51: add adc volume control .../devicetree/bindings/sound/cs42l51.txt | 16 ++ sound/soc/codecs/cs42l51-i2c.c | 13 +- sound/soc/codecs/cs42l51.c | 217 - sound/soc/codecs/cs42l51.h | 3 + 4 files changed, 242 insertions(+), 7 deletions(-) -- 2.7.4
[PATCH 4/7] ASoC: cs42l51: add support of master mode
Add support of master mode for cs42l51 cirrus audio codec. Signed-off-by: Olivier Moysan --- sound/soc/codecs/cs42l51.c | 30 +++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index 9b3ffa16b204..397b68901d1c 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -340,6 +340,19 @@ static struct cs42l51_ratios slave_auto_ratios[] = { { 256, CS42L51_DSM_MODE, 1 }, { 384, CS42L51_DSM_MODE, 1 }, }; +/* + * Master mode mclk/fs ratios. + * Recommended configurations are SSM for 4-50khz and DSM for 50-100kHz ranges + * The table below provides support of following ratios: + * 128: SSM (%128) with div2 disabled + * 256: SSM (%128) with div2 enabled + * In both cases, if sampling rate is above 50kHz, SSM is overridden + * with DSM (%128) configuration + */ +static struct cs42l51_ratios master_ratios[] = { + { 128, CS42L51_SSM_MODE, 0 }, { 256, CS42L51_SSM_MODE, 1 }, +}; + static int cs42l51_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { @@ -362,11 +375,13 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream, unsigned int ratio; struct cs42l51_ratios *ratios = NULL; int nr_ratios = 0; - int intf_ctl, power_ctl, fmt; + int intf_ctl, power_ctl, fmt, mode; switch (cs42l51->func) { case MODE_MASTER: - return -EINVAL; + ratios = master_ratios; + nr_ratios = ARRAY_SIZE(master_ratios); + break; case MODE_SLAVE: ratios = slave_ratios; nr_ratios = ARRAY_SIZE(slave_ratios); @@ -402,7 +417,16 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream, switch (cs42l51->func) { case MODE_MASTER: intf_ctl |= CS42L51_INTF_CTL_MASTER; - power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode); + mode = ratios[i].speed_mode; + /* Force DSM mode if sampling rate is above 50kHz */ + if (rate > 5) + mode = CS42L51_DSM_MODE; + power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(mode); + /* +* Auto detect mode is not applicable for master mode and has to +* be disabled. Otherwise SPEED[1:0] bits will be ignored. +*/ + power_ctl &= ~CS42L51_MIC_POWER_CTL_AUTO; break; case MODE_SLAVE: power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode); -- 2.7.4
[PATCH 3/7] ASoC: cs42l51: add reset management
Manage cs42l51 audio codec reset pin. Signed-off-by: Olivier Moysan --- sound/soc/codecs/cs42l51.c | 15 +++ 1 file changed, 15 insertions(+) diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index f43eb51d2d8d..9b3ffa16b204 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -54,6 +55,7 @@ struct cs42l51_private { unsigned int audio_mode;/* The mode (I2S or left-justified) */ enum master_slave_mode func; struct regulator_bulk_data supplies[ARRAY_SIZE(cs42l51_supply_names)]; + struct gpio_desc *reset_gpio; }; #define CS42L51_FORMATS ( \ @@ -595,6 +597,17 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap) return ret; } + cs42l51->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(cs42l51->reset_gpio)) + return PTR_ERR(cs42l51->reset_gpio); + + if (cs42l51->reset_gpio) { + dev_dbg(dev, "Release reset gpio\n"); + gpiod_set_value_cansleep(cs42l51->reset_gpio, 0); + mdelay(2); + } + /* Verify that we have a CS42L51 */ ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val); if (ret < 0) { @@ -629,6 +642,8 @@ int cs42l51_remove(struct device *dev) { struct cs42l51_private *cs42l51 = dev_get_drvdata(dev); + gpiod_set_value_cansleep(cs42l51->reset_gpio, 1); + return regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies), cs42l51->supplies); } -- 2.7.4
[PATCH 7/7] ASoC: cs42l51: add adc volume control
Add ADC boost volume control for CS42L51 codec. Signed-off-by: Olivier Moysan --- sound/soc/codecs/cs42l51.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index 25f17c0051e4..991e4ebd7a04 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -123,6 +123,7 @@ static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0); static const DECLARE_TLV_DB_SCALE(aout_tlv, -10200, 50, 0); static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0); +static const DECLARE_TLV_DB_SCALE(adc_boost_tlv, 2000, 2000, 0); static const char *chan_mix[] = { "L R", "L+R", @@ -151,6 +152,8 @@ static const struct snd_kcontrol_new cs42l51_snd_controls[] = { SOC_SINGLE("Zero Cross Switch", CS42L51_DAC_CTL, 0, 0, 0), SOC_DOUBLE_TLV("Mic Boost Volume", CS42L51_MIC_CTL, 0, 1, 1, 0, boost_tlv), + SOC_DOUBLE_TLV("ADC Boost Volume", + CS42L51_MIC_CTL, 5, 6, 1, 0, adc_boost_tlv), SOC_SINGLE_TLV("Bass Volume", CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv), SOC_SINGLE_TLV("Treble Volume", CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv), SOC_ENUM_EXT("PCM channel mixer", -- 2.7.4
[PATCH 1/7] ASoC: dt-bindings: update cs42l51 bindings
Add compatible, reg, regulator, and reset to Cirrus CS42L51 audio codec bindings. Signed-off-by: Olivier Moysan --- Documentation/devicetree/bindings/sound/cs42l51.txt | 16 1 file changed, 16 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/cs42l51.txt b/Documentation/devicetree/bindings/sound/cs42l51.txt index 4b5de33ce377..acbd68ddd2cb 100644 --- a/Documentation/devicetree/bindings/sound/cs42l51.txt +++ b/Documentation/devicetree/bindings/sound/cs42l51.txt @@ -1,6 +1,17 @@ CS42L51 audio CODEC +Required properties: + + - compatible : "cirrus,cs42l51" + + - reg : the I2C address of the device for I2C. + Optional properties: + - VL-supply, VD-supply, VA-supply, VAHP-supply: power supplies for the device, +as covered in Documentation/devicetree/bindings/regulator/regulator.txt. + + - reset-gpios : GPIO specification for the reset pin. If specified, it will be +deasserted before starting the communication with the codec. - clocks : a list of phandles + clock-specifiers, one for each entry in clock-names @@ -14,4 +25,9 @@ cs42l51: cs42l51@4a { reg = <0x4a>; clocks = <&mclk_prov>; clock-names = "MCLK"; + VL-supply = <®_audio>; + VD-supply = <®_audio>; + VA-supply = <®_audio>; + VAHP-supply = <®_audio>; + reset-gpios = <&gpiog 9 GPIO_ACTIVE_LOW>; }; -- 2.7.4
[PATCH 5/7] ASoC: cs42l51: change mic bias DAPM
Use SND_SOC_DAPM_SUPPLY for mic bias DAPM instead of deprecated SND_SOC_DAPM_MICBIAS. Signed-off-by: Olivier Moysan --- sound/soc/codecs/cs42l51.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index 397b68901d1c..33298a7a8aab 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -206,7 +206,8 @@ static const struct snd_kcontrol_new cs42l51_adcr_mux_controls = SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum); static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = { - SND_SOC_DAPM_MICBIAS("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1), + SND_SOC_DAPM_SUPPLY("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0, cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0, -- 2.7.4
[PATCH 6/7] ASoC: cs42l51: add power management
Add sleep PM callbacks to support system low power modes. Signed-off-by: Olivier Moysan --- sound/soc/codecs/cs42l51-i2c.c | 7 ++- sound/soc/codecs/cs42l51.c | 122 + sound/soc/codecs/cs42l51.h | 2 + 3 files changed, 129 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c index 8333dbf18ea2..116221e581ce 100644 --- a/sound/soc/codecs/cs42l51-i2c.c +++ b/sound/soc/codecs/cs42l51-i2c.c @@ -29,8 +29,6 @@ static int cs42l51_i2c_probe(struct i2c_client *i2c, struct regmap_config config; config = cs42l51_regmap; - config.val_bits = 8; - config.reg_bits = 8; return cs42l51_probe(&i2c->dev, devm_regmap_init_i2c(i2c, &config)); } @@ -40,10 +38,15 @@ static int cs42l51_i2c_remove(struct i2c_client *i2c) return cs42l51_remove(&i2c->dev); } +static const struct dev_pm_ops cs42l51_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cs42l51_suspend, cs42l51_resume) +}; + static struct i2c_driver cs42l51_i2c_driver = { .driver = { .name = "cs42l51", .of_match_table = cs42l51_of_match, + .pm = &cs42l51_pm_ops, }, .probe = cs42l51_i2c_probe, .remove = cs42l51_i2c_remove, diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index 33298a7a8aab..25f17c0051e4 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -56,6 +56,7 @@ struct cs42l51_private { enum master_slave_mode func; struct regulator_bulk_data supplies[ARRAY_SIZE(cs42l51_supply_names)]; struct gpio_desc *reset_gpio; + struct regmap *regmap; }; #define CS42L51_FORMATS ( \ @@ -576,7 +577,106 @@ static const struct snd_soc_component_driver soc_component_device_cs42l51 = { .non_legacy_dai_naming = 1, }; +static bool cs42l51_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L51_POWER_CTL1: + case CS42L51_MIC_POWER_CTL: + case CS42L51_INTF_CTL: + case CS42L51_MIC_CTL: + case CS42L51_ADC_CTL: + case CS42L51_ADC_INPUT: + case CS42L51_DAC_OUT_CTL: + case CS42L51_DAC_CTL: + case CS42L51_ALC_PGA_CTL: + case CS42L51_ALC_PGB_CTL: + case CS42L51_ADCA_ATT: + case CS42L51_ADCB_ATT: + case CS42L51_ADCA_VOL: + case CS42L51_ADCB_VOL: + case CS42L51_PCMA_VOL: + case CS42L51_PCMB_VOL: + case CS42L51_BEEP_FREQ: + case CS42L51_BEEP_VOL: + case CS42L51_BEEP_CONF: + case CS42L51_TONE_CTL: + case CS42L51_AOUTA_VOL: + case CS42L51_AOUTB_VOL: + case CS42L51_PCM_MIXER: + case CS42L51_LIMIT_THRES_DIS: + case CS42L51_LIMIT_REL: + case CS42L51_LIMIT_ATT: + case CS42L51_ALC_EN: + case CS42L51_ALC_REL: + case CS42L51_ALC_THRES: + case CS42L51_NOISE_CONF: + case CS42L51_CHARGE_FREQ: + return true; + default: + return false; + } +} + +static bool cs42l51_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L51_STATUS: + return true; + default: + return false; + } +} + +static bool cs42l51_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L51_CHIP_REV_ID: + case CS42L51_POWER_CTL1: + case CS42L51_MIC_POWER_CTL: + case CS42L51_INTF_CTL: + case CS42L51_MIC_CTL: + case CS42L51_ADC_CTL: + case CS42L51_ADC_INPUT: + case CS42L51_DAC_OUT_CTL: + case CS42L51_DAC_CTL: + case CS42L51_ALC_PGA_CTL: + case CS42L51_ALC_PGB_CTL: + case CS42L51_ADCA_ATT: + case CS42L51_ADCB_ATT: + case CS42L51_ADCA_VOL: + case CS42L51_ADCB_VOL: + case CS42L51_PCMA_VOL: + case CS42L51_PCMB_VOL: + case CS42L51_BEEP_FREQ: + case CS42L51_BEEP_VOL: + case CS42L51_BEEP_CONF: + case CS42L51_TONE_CTL: + case CS42L51_AOUTA_VOL: + case CS42L51_AOUTB_VOL: + case CS42L51_PCM_MIXER: + case CS42L51_LIMIT_THRES_DIS: + case CS42L51_LIMIT_REL: + case CS42L51_LIMIT_ATT: + case CS42L51_ALC_EN: + case CS42L51_ALC_REL: + case CS42L51_ALC_THRES: + case CS42L51_NOISE_CONF: + case CS42L51_STATUS: + case CS42L51_CHARGE_FREQ: + return true; + default: + return false; + } +} + const struct regmap_config cs42l51_regmap = { + .reg_bits = 8, + .reg_stride = 1, + .val_bits = 8, + .use_single_write = true, + .readable_reg = cs42l51_readable_reg, + .volatile_reg = cs42l51_volatile_reg, + .writeable_reg = cs42l51_writeable_reg, .max_register = CS42L51_CHARGE_FREQ, .cache_type = REGCACHE_RBTREE, }; @@ -597,6 +697,7 @@ int cs42l51_probe(struct de
[PATCH] ASoC: stm32: sai: fix master clock management
When master clock is used, master clock rate is set exclusively. Parent clocks of master clock cannot be changed after a call to clk_set_rate_exclusive(). So the parent clock of SAI kernel clock must be set before. Ensure also that exclusive rate operations are balanced in STM32 SAI driver. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_sai_sub.c | 64 +++ 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 3dd54bc54fa1..77aa38183955 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -70,6 +70,7 @@ #define SAI_IEC60958_STATUS_BYTES 24 #define SAI_MCLK_NAME_LEN 32 +#define SAI_RATE_11K 11025 /** * struct stm32_sai_sub_data - private data of SAI sub block (block A or B) @@ -311,6 +312,25 @@ static int stm32_sai_set_clk_div(struct stm32_sai_sub_data *sai, return ret; } +static int stm32_sai_set_parent_clock(struct stm32_sai_sub_data *sai, + unsigned int rate) +{ + struct platform_device *pdev = sai->pdev; + struct clk *parent_clk = sai->pdata->clk_x8k; + int ret; + + if (!(rate % SAI_RATE_11K)) + parent_clk = sai->pdata->clk_x11k; + + ret = clk_set_parent(sai->sai_ck, parent_clk); + if (ret) + dev_err(&pdev->dev, " Error %d setting sai_ck parent clock. %s", + ret, ret == -EBUSY ? + "Active stream rates conflict\n" : "\n"); + + return ret; +} + static long stm32_sai_mclk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { @@ -492,25 +512,29 @@ static int stm32_sai_set_sysclk(struct snd_soc_dai *cpu_dai, struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); int ret; - if (dir == SND_SOC_CLOCK_OUT) { + if (dir == SND_SOC_CLOCK_OUT && sai->sai_mclk) { ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_NODIV, (unsigned int)~SAI_XCR1_NODIV); if (ret < 0) return ret; - dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq); - sai->mclk_rate = freq; + /* If master clock is used, set parent clock now */ + ret = stm32_sai_set_parent_clock(sai, freq); + if (ret) + return ret; - if (sai->sai_mclk) { - ret = clk_set_rate_exclusive(sai->sai_mclk, -sai->mclk_rate); - if (ret) { - dev_err(cpu_dai->dev, - "Could not set mclk rate\n"); - return ret; - } + ret = clk_set_rate_exclusive(sai->sai_mclk, freq); + if (ret) { + dev_err(cpu_dai->dev, + ret == -EBUSY ? + "Active streams have incompatible rates" : + "Could not set mclk rate\n"); + return ret; } + + dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq); + sai->mclk_rate = freq; } return 0; @@ -917,11 +941,13 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, int div = 0, cr1 = 0; int sai_clk_rate, mclk_ratio, den; unsigned int rate = params_rate(params); + int ret; - if (!(rate % 11025)) - clk_set_parent(sai->sai_ck, sai->pdata->clk_x11k); - else - clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k); + if (!sai->sai_mclk) { + ret = stm32_sai_set_parent_clock(sai, rate); + if (ret) + return ret; + } sai_clk_rate = clk_get_rate(sai->sai_ck); if (STM_SAI_IS_F4(sai->pdata)) { @@ -1080,9 +1106,13 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream, regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_NODIV, SAI_XCR1_NODIV); - clk_disable_unprepare(sai->sai_ck); + /* Release mclk rate only if rate was actually set */ + if (sai->mclk_rate) { + clk_rate_exclusive_put(sai->sai_mclk); + sai->mclk_rate = 0; + } - clk_rate_exclusive_put(sai->sai_mclk); + clk_disable_unprepare(sai->sai_ck); spin_lock_irqsave(&sai->irq_lock, flags); sai->substream = NULL; -- 2.7.4
[PATCH 1/7] ASoC: stm32: i2s: fix IRQ clearing
Because of regmap cache, interrupts may not be cleared as expected. Declare IFCR register as write only and make writings to IFCR register unconditional. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_i2s.c | 13 ++--- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index a25919d32187..339cd4715b2e 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -247,8 +247,8 @@ static irqreturn_t stm32_i2s_isr(int irq, void *devid) return IRQ_NONE; } - regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, - I2S_IFCR_MASK, flags); + regmap_write_bits(i2s->regmap, STM32_I2S_IFCR_REG, + I2S_IFCR_MASK, flags); if (flags & I2S_SR_OVR) { dev_dbg(&pdev->dev, "Overrun\n"); @@ -277,7 +277,6 @@ static bool stm32_i2s_readable_reg(struct device *dev, unsigned int reg) case STM32_I2S_CFG2_REG: case STM32_I2S_IER_REG: case STM32_I2S_SR_REG: - case STM32_I2S_IFCR_REG: case STM32_I2S_TXDR_REG: case STM32_I2S_RXDR_REG: case STM32_I2S_CGFR_REG: @@ -559,8 +558,8 @@ static int stm32_i2s_startup(struct snd_pcm_substream *substream, i2s->refcount++; spin_unlock(&i2s->lock_fd); - return regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, - I2S_IFCR_MASK, I2S_IFCR_MASK); + return regmap_write_bits(i2s->regmap, STM32_I2S_IFCR_REG, +I2S_IFCR_MASK, I2S_IFCR_MASK); } static int stm32_i2s_hw_params(struct snd_pcm_substream *substream, @@ -611,8 +610,8 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } - regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, - I2S_IFCR_MASK, I2S_IFCR_MASK); + regmap_write_bits(i2s->regmap, STM32_I2S_IFCR_REG, + I2S_IFCR_MASK, I2S_IFCR_MASK); if (playback_flg) { ier = I2S_IER_UDRIE; -- 2.7.4
[PATCH 0/7] ASoC: stm32: i2s: miscellaneous fixes
This patch-set is a collection of fixes for STM32 I2S driver. Olivier Moysan (7): ASoC: stm32: i2s: fix IRQ clearing ASoC: stm32: i2s: fix 16 bit format support ASoC: stm32: i2s: fix stream count management ASoC: stm32: i2s: fix dma configuration ASoC: stm32: i2s: remove useless callback ASoC: stm32: i2s: fix race condition in irq handler ASoC: stm32: i2s: skip useless write in slave mode sound/soc/stm/stm32_i2s.c | 73 +++ 1 file changed, 35 insertions(+), 38 deletions(-) -- 2.7.4
[PATCH 3/7] ASoC: stm32: i2s: fix stream count management
Move counter handling to trigger start section to manage multiple start/stop events. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_i2s.c | 12 +--- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 7d4c67433916..7f56d7b51ba3 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -554,10 +554,6 @@ static int stm32_i2s_startup(struct snd_pcm_substream *substream, return ret; } - spin_lock(&i2s->lock_fd); - i2s->refcount++; - spin_unlock(&i2s->lock_fd); - return regmap_write_bits(i2s->regmap, STM32_I2S_IFCR_REG, I2S_IFCR_MASK, I2S_IFCR_MASK); } @@ -613,18 +609,19 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, regmap_write_bits(i2s->regmap, STM32_I2S_IFCR_REG, I2S_IFCR_MASK, I2S_IFCR_MASK); + spin_lock(&i2s->lock_fd); + i2s->refcount++; if (playback_flg) { ier = I2S_IER_UDRIE; } else { ier = I2S_IER_OVRIE; - spin_lock(&i2s->lock_fd); if (i2s->refcount == 1) /* dummy write to trigger capture */ regmap_write(i2s->regmap, STM32_I2S_TXDR_REG, 0); - spin_unlock(&i2s->lock_fd); } + spin_unlock(&i2s->lock_fd); if (STM32_I2S_IS_SLAVE(i2s)) ier |= I2S_IER_TIFREIE; @@ -649,7 +646,6 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, spin_unlock(&i2s->lock_fd); break; } - spin_unlock(&i2s->lock_fd); dev_dbg(cpu_dai->dev, "stop I2S\n"); @@ -657,8 +653,10 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, I2S_CR1_SPE, 0); if (ret < 0) { dev_err(cpu_dai->dev, "Error %d disabling I2S\n", ret); + spin_unlock(&i2s->lock_fd); return ret; } + spin_unlock(&i2s->lock_fd); cfg1_mask = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN; regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, -- 2.7.4
[PATCH 5/7] ASoC: stm32: i2s: remove useless callback
Clocks do not need to be released on driver removal, as this is already managed before. Remove useless remove callback. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_i2s.c | 11 --- 1 file changed, 11 deletions(-) diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 95fffb61faa5..9edb753ffa1b 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -902,16 +902,6 @@ static int stm32_i2s_probe(struct platform_device *pdev) I2S_CGFR_I2SMOD, I2S_CGFR_I2SMOD); } -static int stm32_i2s_remove(struct platform_device *pdev) -{ - struct stm32_i2s_data *i2s = platform_get_drvdata(pdev); - - clk_disable_unprepare(i2s->i2sclk); - clk_disable_unprepare(i2s->pclk); - - return 0; -} - MODULE_DEVICE_TABLE(of, stm32_i2s_ids); #ifdef CONFIG_PM_SLEEP @@ -945,7 +935,6 @@ static struct platform_driver stm32_i2s_driver = { .pm = &stm32_i2s_pm_ops, }, .probe = stm32_i2s_probe, - .remove = stm32_i2s_remove, }; module_platform_driver(stm32_i2s_driver); -- 2.7.4
[PATCH 4/7] ASoC: stm32: i2s: fix dma configuration
DMA configuration is not balanced on start/stop. Move DMA configuration to trigger callback. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_i2s.c | 14 +++--- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 7f56d7b51ba3..95fffb61faa5 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -488,7 +488,7 @@ static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai, { struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); int format = params_width(params); - u32 cfgr, cfgr_mask, cfg1, cfg1_mask; + u32 cfgr, cfgr_mask, cfg1; unsigned int fthlv; int ret; @@ -529,15 +529,11 @@ static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai, if (ret < 0) return ret; - cfg1 = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN; - cfg1_mask = cfg1; - fthlv = STM32_I2S_FIFO_SIZE * I2S_FIFO_TH_ONE_QUARTER / 4; - cfg1 |= I2S_CFG1_FTHVL_SET(fthlv - 1); - cfg1_mask |= I2S_CFG1_FTHVL_MASK; + cfg1 = I2S_CFG1_FTHVL_SET(fthlv - 1); return regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, - cfg1_mask, cfg1); + I2S_CFG1_FTHVL_MASK, cfg1); } static int stm32_i2s_startup(struct snd_pcm_substream *substream, @@ -592,6 +588,10 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, /* Enable i2s */ dev_dbg(cpu_dai->dev, "start I2S\n"); + cfg1_mask = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN; + regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, + cfg1_mask, cfg1_mask); + ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG, I2S_CR1_SPE, I2S_CR1_SPE); if (ret < 0) { -- 2.7.4
[PATCH 6/7] ASoC: stm32: i2s: fix race condition in irq handler
When snd_pcm_stop_xrun() is called in interrupt routine, substream context may have already been released. Add protection on substream context. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_i2s.c | 17 ++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 9edb753ffa1b..42ce87a35104 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -201,6 +201,7 @@ enum i2s_datlen { * @base: mmio register base virtual address * @phys_addr: I2S registers physical base address * @lock_fd: lock to manage race conditions in full duplex mode + * @irq_lock: prevent race condition with IRQ * @dais_name: DAI name * @mclk_rate: master clock frequency (Hz) * @fmt: DAI protocol @@ -222,6 +223,7 @@ struct stm32_i2s_data { void __iomem *base; dma_addr_t phys_addr; spinlock_t lock_fd; /* Manage race conditions for full duplex */ + spinlock_t irq_lock; /* used to prevent race condition with IRQ */ char dais_name[STM32_I2S_DAI_NAME_SIZE]; unsigned int mclk_rate; unsigned int fmt; @@ -263,8 +265,10 @@ static irqreturn_t stm32_i2s_isr(int irq, void *devid) if (flags & I2S_SR_TIFRE) dev_dbg(&pdev->dev, "Frame error\n"); - if (err) + spin_lock(&i2s->irq_lock); + if (err && i2s->substream) snd_pcm_stop_xrun(i2s->substream); + spin_unlock(&i2s->irq_lock); return IRQ_HANDLED; } @@ -540,9 +544,12 @@ static int stm32_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); + unsigned long flags; int ret; + spin_lock_irqsave(&i2s->irq_lock, flags); i2s->substream = substream; + spin_unlock_irqrestore(&i2s->irq_lock, flags); ret = clk_prepare_enable(i2s->i2sclk); if (ret < 0) { @@ -673,13 +680,16 @@ static void stm32_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); - - i2s->substream = NULL; + unsigned long flags; regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, I2S_CGFR_MCKOE, (unsigned int)~I2S_CGFR_MCKOE); clk_disable_unprepare(i2s->i2sclk); + + spin_lock_irqsave(&i2s->irq_lock, flags); + i2s->substream = NULL; + spin_unlock_irqrestore(&i2s->irq_lock, flags); } static int stm32_i2s_dai_probe(struct snd_soc_dai *cpu_dai) @@ -874,6 +884,7 @@ static int stm32_i2s_probe(struct platform_device *pdev) i2s->pdev = pdev; i2s->ms_flg = I2S_MS_NOT_SET; spin_lock_init(&i2s->lock_fd); + spin_lock_init(&i2s->irq_lock); platform_set_drvdata(pdev, i2s); ret = stm32_i2s_dais_init(pdev, i2s); -- 2.7.4
[PATCH 7/7] ASoC: stm32: i2s: skip useless write in slave mode
Dummy write in capture master mode is used to gate bus clocks. This write is useless in slave mode as the clocks are not managed by slave. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_i2s.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 42ce87a35104..47c334de6b09 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -623,8 +623,8 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, } else { ier = I2S_IER_OVRIE; - if (i2s->refcount == 1) - /* dummy write to trigger capture */ + if (STM32_I2S_IS_MASTER(i2s) && i2s->refcount == 1) + /* dummy write to gate bus clocks */ regmap_write(i2s->regmap, STM32_I2S_TXDR_REG, 0); } -- 2.7.4
[PATCH 2/7] ASoC: stm32: i2s: fix 16 bit format support
I2S supports 16 bits data in 32 channel length. However the expected driver behavior, is to set channel length to 16 bits when data format is 16 bits. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_i2s.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 339cd4715b2e..7d4c67433916 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -501,7 +501,7 @@ static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai, switch (format) { case 16: cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_16); - cfgr_mask = I2S_CGFR_DATLEN_MASK; + cfgr_mask = I2S_CGFR_DATLEN_MASK | I2S_CGFR_CHLEN; break; case 32: cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_32) | -- 2.7.4
[RESEND PATCH 0/2] ASoC: stm32: i2s: add master clock provider
Add master clock generation support in STM32 I2S driver. Resend of patch https://lkml.org/lkml/2020/9/11/264 Olivier Moysan (2): ASoC: dt-bindings: add mclk provider support to stm32 i2s ASoC: stm32: i2s: add master clock provider .../bindings/sound/st,stm32-i2s.yaml | 4 + sound/soc/stm/stm32_i2s.c | 310 +++--- 2 files changed, 270 insertions(+), 44 deletions(-) -- 2.17.1
[RESEND PATCH 1/2] ASoC: dt-bindings: add mclk provider support to stm32 i2s
From: Olivier Moysan Add master clock provider support to STM32 I2S. Reviewed-by: Rob Herring Signed-off-by: Olivier Moysan --- Documentation/devicetree/bindings/sound/st,stm32-i2s.yaml | 4 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/st,stm32-i2s.yaml b/Documentation/devicetree/bindings/sound/st,stm32-i2s.yaml index f32410890589..6feb5a09c184 100644 --- a/Documentation/devicetree/bindings/sound/st,stm32-i2s.yaml +++ b/Documentation/devicetree/bindings/sound/st,stm32-i2s.yaml @@ -54,6 +54,10 @@ properties: resets: maxItems: 1 + "#clock-cells": +description: Configure the I2S device as MCLK clock provider. +const: 0 + required: - compatible - "#sound-dai-cells" -- 2.17.1
[PATCH 2/2] ARM: multi_v7_defconfig: enable dfsdm audio support
Add DFSDM audio support by enabling CONFIG_SND_SOC_STM32_DFSDM as module. Signed-off-by: Olivier Moysan --- arch/arm/configs/multi_v7_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index b30a3bc6762b..083d5f4450f4 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -743,6 +743,7 @@ CONFIG_SND_SOC_STI=m CONFIG_SND_SOC_STM32_SAI=m CONFIG_SND_SOC_STM32_I2S=m CONFIG_SND_SOC_STM32_SPDIFRX=m +CONFIG_SND_SOC_STM32_DFSDM=m CONFIG_SND_SUN4I_CODEC=m CONFIG_SND_SOC_TEGRA=m CONFIG_SND_SOC_TEGRA20_I2S=m -- 2.17.1
[PATCH 1/2] ARM: multi_v7_defconfig: enable spdifrx support
Add STM32 SPDIFRX support by enabling CONFIG_SND_SOC_STM32_SPDIFRX as module. Signed-off-by: Olivier Moysan --- arch/arm/configs/multi_v7_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 1fff2591e434..b30a3bc6762b 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -742,6 +742,7 @@ CONFIG_SND_SOC_RCAR=m CONFIG_SND_SOC_STI=m CONFIG_SND_SOC_STM32_SAI=m CONFIG_SND_SOC_STM32_I2S=m +CONFIG_SND_SOC_STM32_SPDIFRX=m CONFIG_SND_SUN4I_CODEC=m CONFIG_SND_SOC_TEGRA=m CONFIG_SND_SOC_TEGRA20_I2S=m -- 2.17.1
[PATCH 0/2] ARM: multi_v7_defconfig: enable dfsdm and spdifrx support
Add STM32 SPDIFRX and DFSDM audio support to multi_v7_defconfig Olivier Moysan (2): ARM: multi_v7_defconfig: enable spdifrx support ARM: multi_v7_defconfig: enable dfsdm audio support arch/arm/configs/multi_v7_defconfig | 2 ++ 1 file changed, 2 insertions(+) -- 2.17.1
[PATCH v2 0/2] ARM: multi_v7_defconfig: enable dfsdm and spdifrx support
Add STM32 SPDIFRX and DFSDM audio support to multi_v7_defconfig Change in v2: - Add targeted SoC in commit message for DFSDM config Olivier Moysan (2): ARM: multi_v7_defconfig: enable spdifrx support ARM: multi_v7_defconfig: enable dfsdm audio support arch/arm/configs/multi_v7_defconfig | 2 ++ 1 file changed, 2 insertions(+) -- 2.17.1
[PATCH v2 1/2] ARM: multi_v7_defconfig: enable spdifrx support
Add STM32 SPDIFRX support by enabling CONFIG_SND_SOC_STM32_SPDIFRX as module. Signed-off-by: Olivier Moysan Reviewed-by: Krzysztof Kozlowski --- arch/arm/configs/multi_v7_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 1fff2591e434..b30a3bc6762b 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -742,6 +742,7 @@ CONFIG_SND_SOC_RCAR=m CONFIG_SND_SOC_STI=m CONFIG_SND_SOC_STM32_SAI=m CONFIG_SND_SOC_STM32_I2S=m +CONFIG_SND_SOC_STM32_SPDIFRX=m CONFIG_SND_SUN4I_CODEC=m CONFIG_SND_SOC_TEGRA=m CONFIG_SND_SOC_TEGRA20_I2S=m -- 2.17.1
[PATCH v2 2/2] ARM: multi_v7_defconfig: enable dfsdm audio support
Add STM32 DFSDM audio support by enabling CONFIG_SND_SOC_STM32_DFSDM as module. Signed-off-by: Olivier Moysan --- arch/arm/configs/multi_v7_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index b30a3bc6762b..083d5f4450f4 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -743,6 +743,7 @@ CONFIG_SND_SOC_STI=m CONFIG_SND_SOC_STM32_SAI=m CONFIG_SND_SOC_STM32_I2S=m CONFIG_SND_SOC_STM32_SPDIFRX=m +CONFIG_SND_SOC_STM32_DFSDM=m CONFIG_SND_SUN4I_CODEC=m CONFIG_SND_SOC_TEGRA=m CONFIG_SND_SOC_TEGRA20_I2S=m -- 2.17.1
Re: [PATCH v2 0/2] ARM: multi_v7_defconfig: enable dfsdm and spdifrx support
Hi Alex On 11/26/20 12:25 PM, Alexandre Torgue wrote: > Hi Olivier > > On 11/20/20 10:15 AM, Olivier Moysan wrote: >> Add STM32 SPDIFRX and DFSDM audio support to multi_v7_defconfig >> >> Change in v2: >> - Add targeted SoC in commit message for DFSDM config >> >> Olivier Moysan (2): >> ARM: multi_v7_defconfig: enable spdifrx support >> ARM: multi_v7_defconfig: enable dfsdm audio support >> >> arch/arm/configs/multi_v7_defconfig | 2 ++ >> 1 file changed, 2 insertions(+) >> > > Targeted platform (STM32) should be added in the commit title for both > patches as you enable STM32 dfsdm and STM32 spdifrx support. No ? > > If you agree, I'll add it during merge. No need to send a v3. > > Regards > Alex It's okay for me. BRs Olivier
[RESEND PATCH 2/2] ASoC: stm32: i2s: add master clock provider
From: Olivier Moysan Add master clock generation support in STM32 I2S driver. The master clock provided by I2S can be used to feed a codec. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_i2s.c | 310 -- 1 file changed, 266 insertions(+), 44 deletions(-) diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 7c4d63c33f15..7d1672cf78cc 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -196,6 +197,9 @@ enum i2s_datlen { #define STM32_I2S_IS_MASTER(x) ((x)->ms_flg == I2S_MS_MASTER) #define STM32_I2S_IS_SLAVE(x) ((x)->ms_flg == I2S_MS_SLAVE) +#define STM32_I2S_NAME_LEN 32 +#define STM32_I2S_RATE_11K 11025 + /** * struct stm32_i2s_data - private data of I2S * @regmap_conf: I2S register map configuration pointer @@ -206,6 +210,7 @@ enum i2s_datlen { * @dma_data_rx: dma configuration data for tx channel * @substream: PCM substream data pointer * @i2sclk: kernel clock feeding the I2S clock generator + * @i2smclk: master clock from I2S mclk provider * @pclk: peripheral clock driving bus interface * @x8kclk: I2S parent clock for sampling frequencies multiple of 8kHz * @x11kclk: I2S parent clock for sampling frequencies multiple of 11kHz @@ -215,6 +220,9 @@ enum i2s_datlen { * @irq_lock: prevent race condition with IRQ * @mclk_rate: master clock frequency (Hz) * @fmt: DAI protocol + * @divider: prescaler division ratio + * @div: prescaler div field + * @odd: prescaler odd field * @refcount: keep count of opened streams on I2S * @ms_flg: master mode flag. */ @@ -227,6 +235,7 @@ struct stm32_i2s_data { struct snd_dmaengine_dai_dma_data dma_data_rx; struct snd_pcm_substream *substream; struct clk *i2sclk; + struct clk *i2smclk; struct clk *pclk; struct clk *x8kclk; struct clk *x11kclk; @@ -236,10 +245,210 @@ struct stm32_i2s_data { spinlock_t irq_lock; /* used to prevent race condition with IRQ */ unsigned int mclk_rate; unsigned int fmt; + unsigned int divider; + unsigned int div; + bool odd; int refcount; int ms_flg; }; +struct stm32_i2smclk_data { + struct clk_hw hw; + unsigned long freq; + struct stm32_i2s_data *i2s_data; +}; + +#define to_mclk_data(_hw) container_of(_hw, struct stm32_i2smclk_data, hw) + +static int stm32_i2s_calc_clk_div(struct stm32_i2s_data *i2s, + unsigned long input_rate, + unsigned long output_rate) +{ + unsigned int ratio, div, divider = 1; + bool odd; + + ratio = DIV_ROUND_CLOSEST(input_rate, output_rate); + + /* Check the parity of the divider */ + odd = ratio & 0x1; + + /* Compute the div prescaler */ + div = ratio >> 1; + + /* If div is 0 actual divider is 1 */ + if (div) { + divider = ((2 * div) + odd); + dev_dbg(&i2s->pdev->dev, "Divider: 2*%d(div)+%d(odd) = %d\n", + div, odd, divider); + } + + /* Division by three is not allowed by I2S prescaler */ + if ((div == 1 && odd) || div > I2S_CGFR_I2SDIV_MAX) { + dev_err(&i2s->pdev->dev, "Wrong divider setting\n"); + return -EINVAL; + } + + if (input_rate % divider) + dev_dbg(&i2s->pdev->dev, + "Rate not accurate. requested (%ld), actual (%ld)\n", + output_rate, input_rate / divider); + + i2s->div = div; + i2s->odd = odd; + i2s->divider = divider; + + return 0; +} + +static int stm32_i2s_set_clk_div(struct stm32_i2s_data *i2s) +{ + u32 cgfr, cgfr_mask; + + cgfr = I2S_CGFR_I2SDIV_SET(i2s->div) | (i2s->odd << I2S_CGFR_ODD_SHIFT); + cgfr_mask = I2S_CGFR_I2SDIV_MASK | I2S_CGFR_ODD; + + return regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, + cgfr_mask, cgfr); +} + +static int stm32_i2s_set_parent_clock(struct stm32_i2s_data *i2s, + unsigned int rate) +{ + struct platform_device *pdev = i2s->pdev; + struct clk *parent_clk; + int ret; + + if (!(rate % STM32_I2S_RATE_11K)) + parent_clk = i2s->x11kclk; + else + parent_clk = i2s->x8kclk; + + ret = clk_set_parent(i2s->i2sclk, parent_clk); + if (ret) + dev_err(&pdev->dev, + "Error %d setting i2sclk parent clock\n", ret); + + return ret; +} + +static long stm32_i2smclk_round_rate(struct clk_hw *hw, unsigned long rate, +unsigned long *prate) +{ +
Re: [PATCH] ASoC: stm32: sai: add missing put_device()
Hi Wen, On 2/9/19 11:41 AM, Wen Yang wrote: > The of_find_device_by_node() takes a reference to the underlying device > structure, we should release that reference. > > Fixes: 7dd0d835582f ("ASoC: stm32: sai: simplify sync modes management") > Signed-off-by: Wen Yang > --- > sound/soc/stm/stm32_sai.c | 11 --- > 1 file changed, 8 insertions(+), 3 deletions(-) > > diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c > index bcb35ca..14c9591 100644 > --- a/sound/soc/stm/stm32_sai.c > +++ b/sound/soc/stm/stm32_sai.c > @@ -112,16 +112,21 @@ static int stm32_sai_set_sync(struct stm32_sai_data > *sai_client, goto error also in previous test if (!pdev) { ... ret = -ENODEV; goto error; } > if (!sai_provider) { > dev_err(&sai_client->pdev->dev, > "SAI sync provider data not found\n"); > - return -EINVAL; > + ret = -EINVAL; > + goto out_put_dev; > } > > /* Configure sync client */ > ret = stm32_sai_sync_conf_client(sai_client, synci); > if (ret < 0) > - return ret; > + goto out_put_dev; > > /* Configure sync provider */ > - return stm32_sai_sync_conf_provider(sai_provider, synco); > + ret = stm32_sai_sync_conf_provider(sai_provider, synco); > + > +out_put_dev: > + put_device(&pdev->dev); > + return ret; Here I propose: error: of_node_put(np_provider); return ret; > } > > static int stm32_sai_probe(struct platform_device *pdev) > Thanks for your patch. Please, see my comments above. Regards Olivier
[PATCH 1/4] ARM: multi_v7_defconfig: enable stm32 sai support
Enable support for SAI on STM32MP1. Signed-off-by: Olivier Moysan --- arch/arm/configs/multi_v7_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index c5d37dfafe98..929d13842171 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -699,6 +699,7 @@ CONFIG_SND_SOC_ODROID=m CONFIG_SND_SOC_SH4_FSI=m CONFIG_SND_SOC_RCAR=m CONFIG_SND_SOC_STI=m +CONFIG_SND_SOC_STM32_SAI=m CONFIG_SND_SUN4I_CODEC=m CONFIG_SND_SOC_TEGRA=m CONFIG_SND_SOC_TEGRA20_I2S=m -- 2.7.4
[PATCH 4/4] ARM: multi_v7_defconfig: enable audio graph card support
Enable audio graph card support for stm32mp157a-dk1 board. Signed-off-by: Olivier Moysan --- arch/arm/configs/multi_v7_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 03a4d93df8c4..c7104a1c1687 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -719,6 +719,7 @@ CONFIG_SND_SOC_SGTL5000=m CONFIG_SND_SOC_SPDIF=m CONFIG_SND_SOC_STI_SAS=m CONFIG_SND_SOC_WM8978=m +CONFIG_SND_AUDIO_GRAPH_CARD=m CONFIG_USB=y CONFIG_USB_OTG=y CONFIG_USB_XHCI_HCD=y -- 2.7.4
[PATCH 3/4] ARM: multi_v7_defconfig: enable cs42l51 codec support
Enable Cirrus CS42L51 audio codec for stm32mp157a-dk1 board. Signed-off-by: Olivier Moysan --- arch/arm/configs/multi_v7_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 02265e195e50..03a4d93df8c4 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -714,6 +714,7 @@ CONFIG_SND_SOC_TEGRA_ALC5632=m CONFIG_SND_SOC_TEGRA_MAX98090=m CONFIG_SND_SOC_AK4642=m CONFIG_SND_SOC_CPCAP=m +CONFIG_SND_SOC_CS42L51_I2C=m CONFIG_SND_SOC_SGTL5000=m CONFIG_SND_SOC_SPDIF=m CONFIG_SND_SOC_STI_SAS=m -- 2.7.4
[PATCH 0/4] ARM: multi_v7_defconfig: add audio support for stm32mp157a-dk1
This patchset adds audio support for stm32mp157a-dk1 board. Olivier Moysan (4): ARM: multi_v7_defconfig: enable stm32 sai support ARM: multi_v7_defconfig: enable stm32 i2s support ARM: multi_v7_defconfig: enable cs42l51 codec support ARM: multi_v7_defconfig: enable audio graph card support arch/arm/configs/multi_v7_defconfig | 4 1 file changed, 4 insertions(+) -- 2.7.4
[PATCH 2/4] ARM: multi_v7_defconfig: enable stm32 i2s support
Enable support for I2S on STM32MP1. Signed-off-by: Olivier Moysan --- arch/arm/configs/multi_v7_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 929d13842171..02265e195e50 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -700,6 +700,7 @@ CONFIG_SND_SOC_SH4_FSI=m CONFIG_SND_SOC_RCAR=m CONFIG_SND_SOC_STI=m CONFIG_SND_SOC_STM32_SAI=m +CONFIG_SND_SOC_STM32_I2S=m CONFIG_SND_SUN4I_CODEC=m CONFIG_SND_SOC_TEGRA=m CONFIG_SND_SOC_TEGRA20_I2S=m -- 2.7.4
[PATCH] ARM: dts: stm32: add hdmi audio support to stm32mp157a-dk1 board
Add HDMI audio support through Sil9022 HDMI transceiver on stm32mp157a-dk1 board. Signed-off-by: Olivier Moysan --- arch/arm/boot/dts/stm32mp157a-dk1.dts | 27 ++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/stm32mp157a-dk1.dts b/arch/arm/boot/dts/stm32mp157a-dk1.dts index 5ad4cef9e971..7a20640c00a9 100644 --- a/arch/arm/boot/dts/stm32mp157a-dk1.dts +++ b/arch/arm/boot/dts/stm32mp157a-dk1.dts @@ -92,7 +92,7 @@ "Playback" , "MCLK", "Capture" , "MCLK", "MICL" , "Mic Bias"; - dais = <&sai2a_port &sai2b_port>; + dais = <&sai2a_port &sai2b_port &i2s2_port>; status = "okay"; }; }; @@ -173,6 +173,7 @@ reset-gpios = <&gpioa 10 GPIO_ACTIVE_LOW>; interrupts = <1 IRQ_TYPE_EDGE_FALLING>; interrupt-parent = <&gpiog>; + #sound-dai-cells = <0>; status = "okay"; ports { @@ -185,6 +186,13 @@ remote-endpoint = <<dc_ep0_out>; }; }; + + port@3 { + reg = <3>; + sii9022_tx_endpoint: endpoint { + remote-endpoint = <&i2s2_endpoint>; + }; + }; }; }; @@ -370,6 +378,23 @@ }; }; +&i2s2 { + clocks = <&rcc SPI2>, <&rcc SPI2_K>, <&rcc PLL3_Q>, <&rcc PLL3_R>; + clock-names = "pclk", "i2sclk", "x8k", "x11k"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&i2s2_pins_a>; + pinctrl-1 = <&i2s2_pins_sleep_a>; + status = "okay"; + + i2s2_port: port { + i2s2_endpoint: endpoint { + remote-endpoint = <&sii9022_tx_endpoint>; + format = "i2s"; + mclk-fs = <256>; + }; + }; +}; + &ipcc { status = "okay"; }; -- 2.17.1
[PATCH] ASoC: stm32: spdifrx: retry synchronization in sync state
When STM32 SPDIFRX is in sync state, allow multiple synchro attempts, instead of exiting on first unsuccessful trial. This is useful when spdif signal is not immediately available on input. This also allows Pulseaudio to check iec capture device availability when no signal is present. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_spdifrx.c | 18 -- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index cd4b235fce57..3fd28ee01675 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -351,6 +351,8 @@ static int stm32_spdifrx_start_sync(struct stm32_spdifrx_data *spdifrx) SPDIFRX_CR_CUMSK | SPDIFRX_CR_PTMSK | SPDIFRX_CR_RXSTEO; cr_mask = cr; + cr |= SPDIFRX_CR_NBTRSET(SPDIFRX_NBTR_63); + cr_mask |= SPDIFRX_CR_NBTR_MASK; cr |= SPDIFRX_CR_SPDIFENSET(SPDIFRX_SPDIFEN_SYNC); cr_mask |= SPDIFRX_CR_SPDIFEN_MASK; ret = regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, @@ -666,7 +668,7 @@ static irqreturn_t stm32_spdifrx_isr(int irq, void *devid) struct snd_pcm_substream *substream = spdifrx->substream; struct platform_device *pdev = spdifrx->pdev; unsigned int cr, mask, sr, imr; - unsigned int flags; + unsigned int flags, sync_state; int err = 0, err_xrun = 0; regmap_read(spdifrx->regmap, STM32_SPDIFRX_SR, &sr); @@ -726,11 +728,23 @@ static irqreturn_t stm32_spdifrx_isr(int irq, void *devid) } if (err) { - /* SPDIFRX in STATE_STOP. Disable SPDIFRX to clear errors */ + regmap_read(spdifrx->regmap, STM32_SPDIFRX_CR, &cr); + sync_state = FIELD_GET(SPDIFRX_CR_SPDIFEN_MASK, cr) && +SPDIFRX_SPDIFEN_SYNC; + + /* SPDIFRX is in STATE_STOP. Disable SPDIFRX to clear errors */ cr = SPDIFRX_CR_SPDIFENSET(SPDIFRX_SPDIFEN_DISABLE); regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, SPDIFRX_CR_SPDIFEN_MASK, cr); + /* If SPDIFRX was in STATE_SYNC, retry synchro */ + if (sync_state) { + cr = SPDIFRX_CR_SPDIFENSET(SPDIFRX_SPDIFEN_SYNC); + regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, + SPDIFRX_CR_SPDIFEN_MASK, cr); + return IRQ_HANDLED; + } + if (substream) snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); -- 2.17.1
[PATCH][RFC] iio: core: add a class hierarchy on iio device lock
The aim of this patch is to correct a recursive locking warning, detected when setting CONFIG_PROVE_LOCKING flag (as shown in message below). This message was initially triggered by the following call sequence in stm32-dfsdm-adc.c driver, when using IIO hardware consumer interface. in stm32_dfsdm_read_raw() iio_device_claim_direct_mode mutex_lock(&indio_dev->mlock); -> lock on dfsdm device iio_hw_consumer_enable iio_update_buffers mutex_lock(&indio_dev->mlock); -> lock on hw consumer device Here two instances of the same lock class are requested on two different objects. The locking validator needs to be informed of the nesting level of each lock to avoid a false positive. This patch introduces a class hierarchy in iio device lock, assuming that hardware consumer is at a lower level than iio device. [ 52.086174] [ 52.086223] [ 52.091516] WARNING: possible recursive locking detected [ 52.096825] 4.19.49 #162 Not tainted [ 52.100384] [ 52.105691] cat/823 is trying to acquire lock: [ 52.110132] 37acb703 (&dev->mlock){+.+.}, at: iio_update_buffers+0x3c/0xd0 [ 52.116995] [ 52.116995] but task is already holding lock: [ 52.122821] 368bb908 (&dev->mlock){+.+.}, at: iio_device_claim_direct_mode+0x18/0x34 [ 52.130560] [ 52.130560] other info that might help us debug this: [ 52.137083] Possible unsafe locking scenario: [ 52.137083] [ 52.142995]CPU0 [ 52.145430] [ 52.147864] lock(&dev->mlock); [ 52.151082] lock(&dev->mlock); [ 52.154301] [ 52.154301] * DEADLOCK * [ 52.154301] [ 52.160215] May be due to missing lock nesting notation [ 52.160215] [ 52.167000] 5 locks held by cat/823: [ 52.170563] #0: 96d6554b (&p->lock){+.+.}, at: seq_read+0x34/0x51c [ 52.176824] #1: 3cf6739a (&of->mutex){+.+.}, at: kernfs_seq_start+0x1c/0x8c [ 52.183866] #2: a6090e0a (kn->count#29){.+.+}, at: kernfs_seq_start+0x24/0x8c [ 52.191083] #3: 368bb908 (&dev->mlock){+.+.}, at: iio_device_claim_direct_mode+0x18/0x34 [ 52.199257] #4: 77e2bcfe (&dev->info_exist_lock){+.+.}, at: iio_update_buffers+0x30/0xd0 [ 52.207431] [ 52.207431] stack backtrace: [ 52.211787] CPU: 0 PID: 823 Comm: cat Not tainted 4.19.49 #162 [ 52.217606] Hardware name: STM32 (Device Tree Support) [ 52.222756] [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [ 52.230487] [] (show_stack) from [] (dump_stack+0xc4/0xf0) [ 52.237703] [] (dump_stack) from [] (__lock_acquire+0x874/0x1344) [ 52.245525] [] (__lock_acquire) from [] (lock_acquire+0xd8/0x268) [ 52.253353] [] (lock_acquire) from [] (__mutex_lock+0x70/0xab0) [ 52.261005] [] (__mutex_lock) from [] (mutex_lock_nested+0x1c/0x24) [ 52.269001] [] (mutex_lock_nested) from [] (iio_update_buffers+0x3c/0xd0) [ 52.277523] [] (iio_update_buffers) from [] (iio_hw_consumer_enable+0x34/0x70) [ 52.286476] [] (iio_hw_consumer_enable) from [] (stm32_dfsdm_read_raw+0xf4/0x3fc) [ 52.295695] [] (stm32_dfsdm_read_raw) from [] (iio_read_channel_info+0xa8/0xb0) [ 52.304738] [] (iio_read_channel_info) from [] (dev_attr_show+0x1c/0x48) [ 52.313170] [] (dev_attr_show) from [] (sysfs_kf_seq_show+0x84/0xec) [ 52.321256] [] (sysfs_kf_seq_show) from [] (seq_read+0x154/0x51c) [ 52.329082] [] (seq_read) from [] (__vfs_read+0x2c/0x15c) [ 52.336209] [] (__vfs_read) from [] (vfs_read+0x90/0x15c) [ 52.343339] [] (vfs_read) from [] (ksys_read+0x5c/0xdc) [ 52.350296] [] (ksys_read) from [] (ret_fast_syscall+0x0/0x28) [ 52.357852] Exception stack(0xe5761fa8 to 0xe5761ff0) [ 52.362904] 1fa0: 006c 7ff0 0003 b6e06000 0002 [ 52.371077] 1fc0: 006c 7ff0 0002 0003 0003 0002 0000 [ 52.379245] 1fe0: 0003 beb6e790 b6eb17b7 b6e3e6c6 Signed-off-by: Olivier Moysan --- drivers/iio/buffer/industrialio-hw-consumer.c | 9 - drivers/iio/industrialio-buffer.c | 2 +- drivers/iio/industrialio-core.c | 3 ++- include/linux/iio/iio.h | 6 ++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c index 95165697d8ae..652ce31b4b5f 100644 --- a/drivers/iio/buffer/industrialio-hw-consumer.c +++ b/drivers/iio/buffer/industrialio-hw-consumer.c @@ -101,6 +101,7 @@ struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev) chan = &hwc->channels[0]; while (chan->indio_dev) { + chan->indio_dev->mutex_class = IIO_MUTEX_HWC; buf = iio_hw_consumer_get_buffer(hwc, chan->indio_dev); if (!buf) { ret = -ENOMEM
Re: [PATCH][RFC] iio: core: add a class hierarchy on iio device lock
Hello Jonathan, Thanks for your comment. On 10/12/19 10:57 AM, Jonathan Cameron wrote: > On Fri, 11 Oct 2019 17:13:14 +0200 > Olivier Moysan wrote: > >> The aim of this patch is to correct a recursive locking warning, >> detected when setting CONFIG_PROVE_LOCKING flag (as shown in message below). >> This message was initially triggered by the following call sequence >> in stm32-dfsdm-adc.c driver, when using IIO hardware consumer interface. >> >> in stm32_dfsdm_read_raw() >> iio_device_claim_direct_mode >> mutex_lock(&indio_dev->mlock); -> lock on >> dfsdm device >> iio_hw_consumer_enable >> iio_update_buffers >> mutex_lock(&indio_dev->mlock); -> lock on hw >> consumer device > Hmm. I'm not sure I follow the logic. That lock is > for one thing and one thing only, preventing access > to the iio device that are unsafe when it is running > in a buffered mode. We shouldn't be in a position where > we both say don't do this if we are in buffered mode, + enter > buffered mode whilst doing this, or we need special functions > for entering buffering mode if in this state. We are in > some sense combining internal driver logic with overall > IIO states. IIO shouldn't care that the device is using > the same methods under the hood for buffered and non > buffered operations. > > I can't really recall how this driver works. Is it actually > possible to have multiple hw_consumers at the same time? > > So do we end up with multiple buffers registered and have > to demux out to the read_raw + the actual buffered path? > Given we have a bit of code saying grab one sample, I'm > going to guess we don't... > > If so, the vast majority of the buffer setup code in IIO > is irrelevant here and we just need to call a few of > the callbacks from this driver directly... (I think > though I haven't chased through every corner. > > I'd rather avoid introducing this nesting for a corner > case that makes no 'semantic' sense in IIO as it leaves us > in two separate states at the same time that the driver > is trying to make mutually exclusive. We can't both > not be in buffered mode, and in buffered mode. > > Thanks and good luck with this nasty corner! > > Jonathan > Here I consider the following use case: A single conversion is performed. The dfsdm (filter) is chained with a front-end, which can be an ADC or a sensor. So we have two IIO devices, the dfsdm and its front-end handled through the hw consumer interface. You are right. There is something wrong here, in buffered/non-buffered mode mixing. iio_hw_consumer_enable() call is used to enable the front-end device. But this interface is intended for buffered mode. So this is not coherent with the expected single conversion mode, indeed. Another interface is required to manage the front-end device. I have a poor knowledge of iio framework, but it seems to me that there is no interface to manage this. My understanding regarding mlock, is that it is used to protect the state of the iio device. I we want to do a conversion from the chained devices, I think we need to activate the first device and keep it performing conversion, as long as the second device has done its conversion. We need to protect both devices, and we should have to do it in a nested way. So, I guess that anyway, nested mutexes would be required in this case. Best regards Olivier > >> Here two instances of the same lock class are requested >> on two different objects. >> The locking validator needs to be informed of the nesting level >> of each lock to avoid a false positive. >> >> This patch introduces a class hierarchy in iio device lock, >> assuming that hardware consumer is at a lower level than iio device. >> >> [ 52.086174] >> [ 52.086223] >> [ 52.091516] WARNING: possible recursive locking detected >> [ 52.096825] 4.19.49 #162 Not tainted >> [ 52.100384] >> [ 52.105691] cat/823 is trying to acquire lock: >> [ 52.110132] 37acb703 (&dev->mlock){+.+.}, at: iio_update_buffers+0x3c/0xd0 >> [ 52.116995] >> [ 52.116995] but task is already holding lock: >> [ 52.122821] 368bb908 (&dev->mlock){+.+.}, at: >> iio_device_claim_direct_mode+0x18/0x34 >> [ 52.130560] >> [ 52.130560] other info that might help us debug this: >> [ 52.137083] Possible unsafe locking scenario: >> [ 52.137083] >> [ 52.142995]CPU0 >> [ 52.145430] >> [
[PATCH] ASoC: stm32: sai: fix sysclk management on shutdown
The commit below, adds a call to sysclk callback on shutdown. This introduces a regression in stm32 SAI driver, as some clock services are called twice, leading to unbalanced calls. Move processing related to mclk from shutdown to sysclk callback. When requested frequency is 0, assume shutdown and release mclk. Fixes: 2458adb8f92a ("SoC: simple-card-utils: set 0Hz to sysclk when shutdown") Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_sai_sub.c | 21 +++-- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index d7501f88aaa6..a4060813bc74 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -505,10 +505,20 @@ static int stm32_sai_set_sysclk(struct snd_soc_dai *cpu_dai, if (dir == SND_SOC_CLOCK_OUT && sai->sai_mclk) { ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_NODIV, -(unsigned int)~SAI_XCR1_NODIV); +freq ? 0 : SAI_XCR1_NODIV); if (ret < 0) return ret; + /* Assume shutdown if requested frequency is 0Hz */ + if (!freq) { + /* Release mclk rate only if rate was actually set */ + if (sai->mclk_rate) { + clk_rate_exclusive_put(sai->sai_mclk); + sai->mclk_rate = 0; + } + return 0; + } + /* If master clock is used, set parent clock now */ ret = stm32_sai_set_parent_clock(sai, freq); if (ret) @@ -1093,15 +1103,6 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream, regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, SAI_XIMR_MASK, 0); - regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_NODIV, - SAI_XCR1_NODIV); - - /* Release mclk rate only if rate was actually set */ - if (sai->mclk_rate) { - clk_rate_exclusive_put(sai->sai_mclk); - sai->mclk_rate = 0; - } - clk_disable_unprepare(sai->sai_ck); spin_lock_irqsave(&sai->irq_lock, flags); -- 2.17.1
Re: [PATCH] ASoC: stm32: sai: add missing put_device()
Acked-by: Olivier Moysan On 2/13/19 3:41 PM, Wen Yang wrote: > From: Olivier MOYSAN > Sent: 11 February 2019 15:09 > To: Wen Yang; Arnaud POULIQUEN; Liam Girdwood; Mark Brown; Jaroslav Kysela; > Takashi Iwai; Maxime Coquelin; Alexandre TORGUE > Cc: alsa-de...@alsa-project.org; linux-st...@st-md-mailman.stormreply.com; > linux-arm-ker...@lists.infradead.org; linux-kernel@vger.kernel.org > Subject: Re: [PATCH] ASoC: stm32: sai: add missing put_device() > >> On 2/9/19 11:41 AM, Wen Yang wrote: >>> The of_find_device_by_node() takes a reference to the underlying device >>> structure, we should release that reference. >>> >>> Fixes: 7dd0d835582f ("ASoC: stm32: sai: simplify sync modes management") >>> Signed-off-by: Wen Yang >>> --- >>>sound/soc/stm/stm32_sai.c | 11 --- >>>1 file changed, 8 insertions(+), 3 deletions(-) >>> >>> diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c >>> index bcb35ca..14c9591 100644 >>> --- a/sound/soc/stm/stm32_sai.c >>> +++ b/sound/soc/stm/stm32_sai.c >>> @@ -112,16 +112,21 @@ static int stm32_sai_set_sync(struct stm32_sai_data >>> *sai_client, >> >> goto error also in previous test >>if (!pdev) { >>... >>ret = -ENODEV; >> goto error; >> } >> >>>if (!sai_provider) { >>>dev_err(&sai_client->pdev->dev, >>>"SAI sync provider data not found\n"); >>> - return -EINVAL; >>> + ret = -EINVAL; >>> + goto out_put_dev; >>>} >>> >>>/* Configure sync client */ >>>ret = stm32_sai_sync_conf_client(sai_client, synci); >>>if (ret < 0) >>> - return ret; >>> + goto out_put_dev; >>> >>>/* Configure sync provider */ >>> - return stm32_sai_sync_conf_provider(sai_provider, synco); >>> + ret = stm32_sai_sync_conf_provider(sai_provider, synco); >>> + >>> +out_put_dev: >>> + put_device(&pdev->dev); >>> + return ret; >> >> Here I propose: >> error: >> of_node_put(np_provider); >> return ret; >> >>>} >>> >>>static int stm32_sai_probe(struct platform_device *pdev) >>> > >> Thanks for your patch. Please, see my comments above. > > Thanks for your comments, in this patch we only fix the problem of missing > put_device(). > The problem of missing of_node_put() is a bit more complicated: > For the variable np_provider(np_sync_provider): > 1, it is obtained by of_get_parent(), but it is not released; > 2, error code not obtained when calling sai->pdata->set_sync() > I agree, release of np_sync_provider node is missing, as well as returned error on set_sync. So yes, this requires a dedicated patch. Thanks Regards Olivier > We will submit another patch to fix the failure of np_sync_provider > Thank you. > > Regards > Wen > >
[PATCH 2/2] SoC: stm32: i2s: manage clock power
Kernel clock management: Enable/disable I2S kernel clock on audio stream startup/shutdown. Peripheral clock management: Manage I2S peripheral clock power through regmap services. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_i2s.c | 44 +++- 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index dbe23a709d24..a25919d32187 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -545,9 +545,16 @@ static int stm32_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); + int ret; i2s->substream = substream; + ret = clk_prepare_enable(i2s->i2sclk); + if (ret < 0) { + dev_err(cpu_dai->dev, "Failed to enable clock: %d\n", ret); + return ret; + } + spin_lock(&i2s->lock_fd); i2s->refcount++; spin_unlock(&i2s->lock_fd); @@ -674,6 +681,8 @@ static void stm32_i2s_shutdown(struct snd_pcm_substream *substream, regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, I2S_CGFR_MCKOE, (unsigned int)~I2S_CGFR_MCKOE); + + clk_disable_unprepare(i2s->i2sclk); } static int stm32_i2s_dai_probe(struct snd_soc_dai *cpu_dai) @@ -874,49 +883,26 @@ static int stm32_i2s_probe(struct platform_device *pdev) if (ret) return ret; - i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->base, - i2s->regmap_conf); + i2s->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "pclk", + i2s->base, i2s->regmap_conf); if (IS_ERR(i2s->regmap)) { dev_err(&pdev->dev, "regmap init failed\n"); return PTR_ERR(i2s->regmap); } - ret = clk_prepare_enable(i2s->pclk); - if (ret) { - dev_err(&pdev->dev, "Enable pclk failed: %d\n", ret); - return ret; - } - - ret = clk_prepare_enable(i2s->i2sclk); - if (ret) { - dev_err(&pdev->dev, "Enable i2sclk failed: %d\n", ret); - goto err_pclk_disable; - } - ret = devm_snd_soc_register_component(&pdev->dev, &stm32_i2s_component, i2s->dai_drv, 1); if (ret) - goto err_clocks_disable; + return ret; ret = devm_snd_dmaengine_pcm_register(&pdev->dev, &stm32_i2s_pcm_config, 0); if (ret) - goto err_clocks_disable; + return ret; /* Set SPI/I2S in i2s mode */ - ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, -I2S_CGFR_I2SMOD, I2S_CGFR_I2SMOD); - if (ret) - goto err_clocks_disable; - - return ret; - -err_clocks_disable: - clk_disable_unprepare(i2s->i2sclk); -err_pclk_disable: - clk_disable_unprepare(i2s->pclk); - - return ret; + return regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, + I2S_CGFR_I2SMOD, I2S_CGFR_I2SMOD); } static int stm32_i2s_remove(struct platform_device *pdev) -- 2.7.4
[PATCH 1/2] ASoC: stm32: i2s: add power management
Add suspend and resume sleep callbacks, to support system low power modes. Signed-off-by: Olivier Moysan --- sound/soc/stm/stm32_i2s.c | 33 ++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 6d0bf78d114d..dbe23a709d24 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -186,8 +186,9 @@ enum i2s_datlen { #define STM32_I2S_IS_SLAVE(x) ((x)->ms_flg == I2S_MS_SLAVE) /** + * struct stm32_i2s_data - private data of I2S * @regmap_conf: I2S register map configuration pointer - * @egmap: I2S register map pointer + * @regmap: I2S register map pointer * @pdev: device data pointer * @dai_drv: DAI driver pointer * @dma_data_tx: dma configuration data for tx channel @@ -596,8 +597,8 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } - ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG, -I2S_CR1_CSTART, I2S_CR1_CSTART); + ret = regmap_write_bits(i2s->regmap, STM32_I2S_CR1_REG, + I2S_CR1_CSTART, I2S_CR1_CSTART); if (ret < 0) { dev_err(cpu_dai->dev, "Error %d starting I2S\n", ret); return ret; @@ -703,6 +704,7 @@ static const struct regmap_config stm32_h7_i2s_regmap_conf = { .volatile_reg = stm32_i2s_volatile_reg, .writeable_reg = stm32_i2s_writeable_reg, .fast_io = true, + .cache_type = REGCACHE_FLAT, }; static const struct snd_soc_dai_ops stm32_i2s_pcm_dai_ops = { @@ -929,10 +931,35 @@ static int stm32_i2s_remove(struct platform_device *pdev) MODULE_DEVICE_TABLE(of, stm32_i2s_ids); +#ifdef CONFIG_PM_SLEEP +static int stm32_i2s_suspend(struct device *dev) +{ + struct stm32_i2s_data *i2s = dev_get_drvdata(dev); + + regcache_cache_only(i2s->regmap, true); + regcache_mark_dirty(i2s->regmap); + + return 0; +} + +static int stm32_i2s_resume(struct device *dev) +{ + struct stm32_i2s_data *i2s = dev_get_drvdata(dev); + + regcache_cache_only(i2s->regmap, false); + return regcache_sync(i2s->regmap); +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops stm32_i2s_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(stm32_i2s_suspend, stm32_i2s_resume) +}; + static struct platform_driver stm32_i2s_driver = { .driver = { .name = "st,stm32-i2s", .of_match_table = stm32_i2s_ids, + .pm = &stm32_i2s_pm_ops, }, .probe = stm32_i2s_probe, .remove = stm32_i2s_remove, -- 2.7.4
[PATCH 0/2] ASoC: stm32: i2s: add power management
Add support of system low power modes to STM32 I2S driver: Implement sleep PM suspend and resume callbacks, to restore STM32 I2S registers after low power modes. Add STM32 I2S peripheral and kernel clocks power management: - Enable/disable kernel clock on audio stream startup/shutdown. - Manage peripheral clock power through regmap services . Olivier Moysan (2): ASoC: stm32: i2s: add power management SoC: stm32: i2s: manage clock power sound/soc/stm/stm32_i2s.c | 77 +++ 1 file changed, 45 insertions(+), 32 deletions(-) -- 2.7.4
Re: [PATCH 5/7] ASoC: cs42l51: change mic bias DAPM
Hello Mark, There are no routes in cs42l51_routes table using "Mic Bias" widget. I use this widget in a route defined in audio graph card DT node. I checked other DTs which are using CS42L51 codec (armada-370-db.dts kirkwood-openrd-client.dts, kirkwood-openrd-ultimate.dts),but I did not find reference to "Mic Bias" widget. So, my understanding is that the patch should not break the legacy. If you think this assumption is not correct please let me know. Regards Olivier On 4/4/19 7:47 AM, Mark Brown wrote: > On Wed, Apr 03, 2019 at 03:23:35PM +0200, Olivier Moysan wrote: >> Use SND_SOC_DAPM_SUPPLY for mic bias DAPM >> instead of deprecated SND_SOC_DAPM_MICBIAS. > > There are existing users in mainline, have they all been updated to be > compatible with this, or verified that they don't need updates? >
[PATCH 1/2] ARM: dts: stm32: add spdifrx support on stm32mp157c
This patch adds support of STM32 SPDIFRX on stm32mp157c. Signed-off-by: Olivier Moysan --- arch/arm/boot/dts/stm32mp157c.dtsi | 13 + 1 file changed, 13 insertions(+) diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi index 6ce75f696679..7769685d6361 100644 --- a/arch/arm/boot/dts/stm32mp157c.dtsi +++ b/arch/arm/boot/dts/stm32mp157c.dtsi @@ -580,6 +580,19 @@ status = "disabled"; }; + spdifrx: audio-controller@4000d000 { + compatible = "st,stm32h7-spdifrx"; + #sound-dai-cells = <0>; + reg = <0x4000d000 0x400>; + clocks = <&rcc SPDIF_K>; + clock-names = "kclk"; + interrupts = ; + dmas = <&dmamux1 93 0x400 0x01>, + <&dmamux1 94 0x400 0x01>; + dma-names = "rx", "rx-ctrl"; + status = "disabled"; + }; + spi1: spi@44004000 { #address-cells = <1>; #size-cells = <0>; -- 2.7.4
[PATCH 2/2] ARM: dts: stm32: add spdfirx pins to stm32mp157c
This patch adds spdifrx support on stm32mp157c eval board. Signed-off-by: Olivier Moysan --- arch/arm/boot/dts/stm32mp157-pinctrl.dtsi | 13 + 1 file changed, 13 insertions(+) diff --git a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi index 9104896e6066..54c133094a8f 100644 --- a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi +++ b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi @@ -393,6 +393,19 @@ }; }; + spdifrx_pins_a: spdifrx-0 { + pins { + pinmux = ; /* SPDIF_IN1 */ + bias-disable; + }; + }; + + spdifrx_sleep_pins_a: spdifrx-1 { + pins { + pinmux = ; /* SPDIF_IN1 */ + }; + }; + uart4_pins_a: uart4-0 { pins1 { pinmux = ; /* UART4_TX */ -- 2.7.4
[PATCH 0/2] ARM: dts: stm32: add spdifrx support on stm32mp157c
This patchset adds support of STM32 SPDFIRX on stm32mp157c Olivier Moysan (2): ARM: dts: stm32: add spdifrx support on stm32mp157c ARM: dts: stm32: add spdfirx pins to stm32mp157c arch/arm/boot/dts/stm32mp157-pinctrl.dtsi | 13 + arch/arm/boot/dts/stm32mp157c.dtsi| 13 + 2 files changed, 26 insertions(+) -- 2.7.4
[PATCH] ASoC: cs42l51: add multi endpoint support
Support multiple endpoints on cs42L51 codec port when used in of_graph context. This patch allows to share the codec port between two CPU DAIs. Example: STM32MP157C-DK2 board uses CS42L51 audio codec. This codec is connected to two serial audio interfaces, which are configured either as rx or tx. >From AsoC point of view the topolgy is the following: // 2 CPU DAIs (SAI2A/B), 1 Codec (CS42L51) Playback: CPU-A-DAI(slave) -> (master)CODEC-DAI/port0 Record: CPU-B-DAI(slave) <- (master)CODEC-DAI/port0 In the DT two endpoints have to be associated to the codec port: cs42l51_port: port { cs42l51_tx_endpoint: endpoint@0 { remote-endpoint = <&sai2a_endpoint>; }; cs42l51_rx_endpoint: endpoint@1 { remote-endpoint = <&sai2b_endpoint>; }; }; However, when the audio graph card parses the codec nodes, it expects to find DAI interface indexes matching the endpoints indexes. The current patch forces the use of DAI id 0 for both endpoints, which allows to share the codec DAI between the two CPU DAIs for playback and capture streams respectively. Signed-off-by: Olivier Moysan --- sound/soc/codecs/cs42l51.c | 8 1 file changed, 8 insertions(+) diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index fd2bd74024c1..80da3cd73e04 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -464,6 +464,13 @@ static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute) return snd_soc_component_write(component, CS42L51_DAC_OUT_CTL, reg); } +static int cs42l51_of_xlate_dai_id(struct snd_soc_component *component, + struct device_node *endpoint) +{ + /* return dai id 0, whatever the endpoint index */ + return 0; +} + static const struct snd_soc_dai_ops cs42l51_dai_ops = { .hw_params = cs42l51_hw_params, .set_sysclk = cs42l51_set_dai_sysclk, @@ -526,6 +533,7 @@ static const struct snd_soc_component_driver soc_component_device_cs42l51 = { .num_dapm_widgets = ARRAY_SIZE(cs42l51_dapm_widgets), .dapm_routes= cs42l51_routes, .num_dapm_routes= ARRAY_SIZE(cs42l51_routes), + .of_xlate_dai_id= cs42l51_of_xlate_dai_id, .idle_bias_on = 1, .use_pmdown_time= 1, .endianness = 1, -- 2.7.4