diff options
Diffstat (limited to 'sound/soc/stm')
-rw-r--r-- | sound/soc/stm/Makefile | 1 | ||||
-rw-r--r-- | sound/soc/stm/stm32_sai.c | 162 | ||||
-rw-r--r-- | sound/soc/stm/stm32_sai.h | 22 | ||||
-rw-r--r-- | sound/soc/stm/stm32_sai_sub.c | 162 | ||||
-rw-r--r-- | sound/soc/stm/stm32_spdifrx.c | 23 |
5 files changed, 324 insertions, 46 deletions
diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile index 4ed22e648a9a..5b7f0fab0bd6 100644 --- a/sound/soc/stm/Makefile +++ b/sound/soc/stm/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # SAI snd-soc-stm32-sai-sub-objs := stm32_sai_sub.o obj-$(CONFIG_SND_SOC_STM32_SAI) += snd-soc-stm32-sai-sub.o diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index 1258bef4dcb3..d6f71a3406e9 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -16,6 +16,7 @@ * details. */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/module.h> @@ -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 const struct of_device_id stm32_sai_ids[] = { {} }; +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 = pdev->dev.of_node; struct stm32_sai_data *sai; struct reset_control *rst; struct resource *res; - void __iomem *base; const struct of_device_id *of_id; + int ret; sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); if (!sai) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); + sai->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(sai->base)) + return PTR_ERR(sai->base); of_id = of_match_device(stm32_sai_ids, &pdev->dev); if (of_id) @@ -65,6 +196,14 @@ static int stm32_sai_probe(struct platform_device *pdev) else return -EINVAL; + if (!STM_SAI_IS_F4(sai)) { + sai->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(sai->pclk)) { + dev_err(&pdev->dev, "missing bus clock pclk\n"); + return PTR_ERR(sai->pclk); + } + } + sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k"); if (IS_ERR(sai->clk_x8k)) { dev_err(&pdev->dev, "missing x8k parent clock\n"); @@ -85,23 +224,34 @@ 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); reset_control_deassert(rst); } + ret = stm32_sai_sync_add_provider(pdev, sai); + if (ret < 0) + return ret; + sai->set_sync = &stm32_sai_set_sync; + sai->pdev = pdev; platform_set_drvdata(pdev, sai); - return of_platform_populate(np, NULL, NULL, &pdev->dev); + ret = of_platform_populate(np, NULL, NULL, &pdev->dev); + if (ret < 0) + stm32_sai_sync_del_provider(np); + + return ret; } static int stm32_sai_remove(struct platform_device *pdev) { of_platform_depopulate(&pdev->dev); + stm32_sai_sync_del_provider(pdev->dev.of_node); + return 0; } diff --git a/sound/soc/stm/stm32_sai.h b/sound/soc/stm/stm32_sai.h index 889974dc62d9..bb062e70de63 100644 --- a/sound/soc/stm/stm32_sai.h +++ b/sound/soc/stm/stm32_sai.h @@ -16,9 +16,11 @@ * details. */ +#include <linux/bitfield.h> + /******************** SAI Register Map **************************************/ -/* common register */ +/* Global configuration register */ #define STM_SAI_GCR 0x00 /* Sub-block A&B registers offsets, relative to A&B sub-block addresses */ @@ -37,12 +39,13 @@ /******************** Bit definition for SAI_GCR register *******************/ #define SAI_GCR_SYNCIN_SHIFT 0 +#define SAI_GCR_SYNCIN_WDTH 2 #define SAI_GCR_SYNCIN_MASK GENMASK(1, SAI_GCR_SYNCIN_SHIFT) -#define SAI_GCR_SYNCIN_SET(x) ((x) << SAI_GCR_SYNCIN_SHIFT) +#define SAI_GCR_SYNCIN_MAX FIELD_GET(SAI_GCR_SYNCIN_MASK,\ + SAI_GCR_SYNCIN_MASK) #define SAI_GCR_SYNCOUT_SHIFT 4 #define SAI_GCR_SYNCOUT_MASK GENMASK(5, SAI_GCR_SYNCOUT_SHIFT) -#define SAI_GCR_SYNCOUT_SET(x) ((x) << SAI_GCR_SYNCOUT_SHIFT) /******************* Bit definition for SAI_XCR1 register *******************/ #define SAI_XCR1_RX_TX_SHIFT 0 @@ -231,6 +234,12 @@ #define STM_SAI_IS_F4(ip) ((ip)->conf->version == SAI_STM32F4) #define STM_SAI_IS_H7(ip) ((ip)->conf->version == SAI_STM32H7) +enum stm32_sai_syncout { + STM_SAI_SYNC_OUT_NONE, + STM_SAI_SYNC_OUT_A, + STM_SAI_SYNC_OUT_B, +}; + enum stm32_sai_version { SAI_STM32F4, SAI_STM32H7 @@ -247,15 +256,22 @@ struct stm32_sai_conf { /** * struct stm32_sai_data - private data of SAI instance driver * @pdev: device data pointer + * @base: common register bank virtual base address + * @pclk: SAI bus clock * @clk_x8k: SAI parent clock for sampling frequencies multiple of 8kHz * @clk_x11k: SAI parent clock for sampling frequencies multiple of 11kHz * @version: SOC version * @irq: SAI interrupt line + * @set_sync: pointer to synchro mode configuration callback */ struct stm32_sai_data { struct platform_device *pdev; + void __iomem *base; + struct clk *pclk; struct clk *clk_x8k; struct clk *clk_x11k; struct stm32_sai_conf *conf; int irq; + int (*set_sync)(struct stm32_sai_data *sai, + struct device_node *np_provider, int synco, int synci); }; diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 90d439613899..08583b958430 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -55,6 +55,12 @@ #define STM_SAI_IS_SUB_B(x) ((x)->id == STM_SAI_B_ID) #define STM_SAI_BLOCK_NAME(x) (((x)->id == STM_SAI_A_ID) ? "A" : "B") +#define SAI_SYNC_NONE 0x0 +#define SAI_SYNC_INTERNAL 0x1 +#define SAI_SYNC_EXTERNAL 0x2 + +#define STM_SAI_HAS_EXT_SYNC(x) (!STM_SAI_IS_F4(sai->pdata)) + /** * struct stm32_sai_sub_data - private data of SAI sub block (block A or B) * @pdev: device data pointer @@ -65,6 +71,7 @@ * @cpu_dai: DAI runtime data pointer * @substream: PCM substream data pointer * @pdata: SAI block parent data pointer + * @np_sync_provider: synchronization provider node * @sai_ck: kernel clock feeding the SAI clock generator * @phys_addr: SAI registers physical base address * @mclk_rate: SAI block master clock frequency (Hz). set at init @@ -73,6 +80,8 @@ * @master: SAI block mode flag. (true=master, false=slave) set at init * @fmt: SAI block format. relevant only for custom protocols. set at init * @sync: SAI block synchronization mode. (none, internal or external) + * @synco: SAI block ext sync source (provider setting). (none, sub-block A/B) + * @synci: SAI block ext sync source (client setting). (SAI sync provider index) * @fs_length: frame synchronization length. depends on protocol settings * @slots: rx or tx slot number * @slot_width: rx or tx slot width in bits @@ -88,6 +97,7 @@ struct stm32_sai_sub_data { struct snd_soc_dai *cpu_dai; struct snd_pcm_substream *substream; struct stm32_sai_data *pdata; + struct device_node *np_sync_provider; struct clk *sai_ck; dma_addr_t phys_addr; unsigned int mclk_rate; @@ -96,6 +106,8 @@ struct stm32_sai_sub_data { bool master; int fmt; int sync; + int synco; + int synci; int fs_length; int slots; int slot_width; @@ -184,7 +196,6 @@ static const struct regmap_config stm32_sai_sub_regmap_config_h7 = { 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 +210,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 +243,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; @@ -304,12 +320,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 +355,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; @@ -380,6 +399,14 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) fmt & SND_SOC_DAIFMT_MASTER_MASK); return -EINVAL; } + + /* Set slave mode if sub-block is synchronized with another SAI */ + if (sai->sync) { + dev_dbg(cpu_dai->dev, "Synchronized SAI configured as slave\n"); + cr1 |= SAI_XCR1_SLAVE; + sai->master = false; + } + cr1_mask |= SAI_XCR1_SLAVE; /* do not generate master by default */ @@ -412,8 +439,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); @@ -442,34 +467,33 @@ 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 = 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); + 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"); 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)) @@ -481,10 +505,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; } @@ -691,6 +711,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); @@ -725,9 +748,15 @@ 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; + /* + * 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; @@ -736,7 +765,21 @@ 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; + + /* 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); + } + + cr1_mask |= SAI_XCR1_SYNCEN_MASK; + cr1 |= SAI_XCR1_SYNCEN_SET(sai->sync); + + 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 = { @@ -822,6 +865,8 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, struct device_node *np = pdev->dev.of_node; struct resource *res; void __iomem *base; + struct of_phandle_args args; + int ret; if (!np) return -ENODEV; @@ -855,6 +900,69 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, return -EINVAL; } + /* Get synchronization property */ + args.np = NULL; + ret = of_parse_phandle_with_fixed_args(np, "st,sync", 1, 0, &args); + if (ret < 0 && ret != -ENOENT) { + dev_err(&pdev->dev, "Failed to get st,sync property\n"); + return ret; + } + + sai->sync = SAI_SYNC_NONE; + if (args.np) { + if (args.np == np) { + dev_err(&pdev->dev, "%s sync own reference\n", + np->name); + of_node_put(args.np); + return -EINVAL; + } + + sai->np_sync_provider = of_get_parent(args.np); + if (!sai->np_sync_provider) { + dev_err(&pdev->dev, "%s parent node not found\n", + np->name); + of_node_put(args.np); + return -ENODEV; + } + + sai->sync = SAI_SYNC_INTERNAL; + if (sai->np_sync_provider != sai->pdata->pdev->dev.of_node) { + if (!STM_SAI_HAS_EXT_SYNC(sai)) { + dev_err(&pdev->dev, + "External synchro not supported\n"); + of_node_put(args.np); + return -EINVAL; + } + sai->sync = SAI_SYNC_EXTERNAL; + + sai->synci = args.args[0]; + if (sai->synci < 1 || + (sai->synci > (SAI_GCR_SYNCIN_MAX + 1))) { + dev_err(&pdev->dev, "Wrong SAI index\n"); + of_node_put(args.np); + return -EINVAL; + } + + if (of_property_match_string(args.np, "compatible", + "st,stm32-sai-sub-a") >= 0) + sai->synco = STM_SAI_SYNC_OUT_A; + + if (of_property_match_string(args.np, "compatible", + "st,stm32-sai-sub-b") >= 0) + sai->synco = STM_SAI_SYNC_OUT_B; + + if (!sai->synco) { + dev_err(&pdev->dev, "Unknown SAI sub-block\n"); + of_node_put(args.np); + return -EINVAL; + } + } + + dev_dbg(&pdev->dev, "%s synchronized with %s\n", + pdev->name, args.np->full_name); + } + + of_node_put(args.np); sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck"); if (IS_ERR(sai->sai_ck)) { dev_err(&pdev->dev, "Missing kernel clock sai_ck\n"); diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index 84cc5678beba..b9bdefcd3e10 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; } @@ -750,17 +749,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, @@ -958,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); |