From 95f444dc9371a3910179a9621c8b94f0f60f5f04 Mon Sep 17 00:00:00 2001 From: Koro Chen Date: Wed, 28 Oct 2015 10:15:34 +0800 Subject: ASoC: dpcm: Make BE prepare possible in suspend state During suspend/resume, there is a flow that if a driver does not support SNDRV_PCM_INFO_RESUME, it will fail at snd_pcm_resume(), and user space can then issue SNDRV_PCM_IOCTL_PREPARE to let audio continue to play. However, in dpcm_be_dai_prepare() it only allows BEs to be prepared in state SND_SOC_DPCM_STATE_HW_PARAMS or SND_SOC_DPCM_STATE_STOP. The BE state will then stay in SND_SOC_DPCM_STATE_SUSPEND, consequently dpcm_be_dai_shutdown() is skipped in the end of playback and be_substream->runtime is not cleared while this runtime is actually freed by snd_pcm_detach_substream(). If another suspend comes, a NULL pointer dereference will happen in snd_pcm_suspend() when accessing BE substream's runtime. Signed-off-by: Koro Chen Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index c86dc96..c482322 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2115,7 +2115,8 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) continue; if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && - (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND)) continue; dev_dbg(be->dev, "ASoC: prepare BE %s\n", -- cgit v0.10.2 From 8973112aa41b8ad956a5b47f2fe17bc2a5cf2645 Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Mon, 26 Oct 2015 15:19:02 +0800 Subject: ASoC: fsl_esai: ETDR and TX0~5 registers are non volatile ETDR and TX0~5 registers are writable and not readable. So they are non volatile. Remove them from volatile list, and add default register value for them. Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 59f234e..504e731 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -653,21 +653,28 @@ static const struct snd_soc_component_driver fsl_esai_component = { }; static const struct reg_default fsl_esai_reg_defaults[] = { - {0x8, 0x00000000}, - {0x10, 0x00000000}, - {0x18, 0x00000000}, - {0x98, 0x00000000}, - {0xd0, 0x00000000}, - {0xd4, 0x00000000}, - {0xd8, 0x00000000}, - {0xdc, 0x00000000}, - {0xe0, 0x00000000}, - {0xe4, 0x0000ffff}, - {0xe8, 0x0000ffff}, - {0xec, 0x0000ffff}, - {0xf0, 0x0000ffff}, - {0xf8, 0x00000000}, - {0xfc, 0x00000000}, + {REG_ESAI_ETDR, 0x00000000}, + {REG_ESAI_ECR, 0x00000000}, + {REG_ESAI_TFCR, 0x00000000}, + {REG_ESAI_RFCR, 0x00000000}, + {REG_ESAI_TX0, 0x00000000}, + {REG_ESAI_TX1, 0x00000000}, + {REG_ESAI_TX2, 0x00000000}, + {REG_ESAI_TX3, 0x00000000}, + {REG_ESAI_TX4, 0x00000000}, + {REG_ESAI_TX5, 0x00000000}, + {REG_ESAI_TSR, 0x00000000}, + {REG_ESAI_SAICR, 0x00000000}, + {REG_ESAI_TCR, 0x00000000}, + {REG_ESAI_TCCR, 0x00000000}, + {REG_ESAI_RCR, 0x00000000}, + {REG_ESAI_RCCR, 0x00000000}, + {REG_ESAI_TSMA, 0x0000ffff}, + {REG_ESAI_TSMB, 0x0000ffff}, + {REG_ESAI_RSMA, 0x0000ffff}, + {REG_ESAI_RSMB, 0x0000ffff}, + {REG_ESAI_PRRC, 0x00000000}, + {REG_ESAI_PCRC, 0x00000000}, }; static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg) @@ -705,17 +712,10 @@ static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg) static bool fsl_esai_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { - case REG_ESAI_ETDR: case REG_ESAI_ERDR: case REG_ESAI_ESR: case REG_ESAI_TFSR: case REG_ESAI_RFSR: - case REG_ESAI_TX0: - case REG_ESAI_TX1: - case REG_ESAI_TX2: - case REG_ESAI_TX3: - case REG_ESAI_TX4: - case REG_ESAI_TX5: case REG_ESAI_RX0: case REG_ESAI_RX1: case REG_ESAI_RX2: -- cgit v0.10.2 From 3f6f5b0cb3e3dc8fdd4eb826f30257df423b37cb Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Mon, 26 Oct 2015 15:19:03 +0800 Subject: ASoC: fsl-sai: add default register map for regmap cache FSL_SAI_TDR register is writable and not readable. According to regmap_volatile() function, if FSL_SAI_TDR want to be volatile, it should be readable. So we should remove FSL_SAI_TDR from volatile register list. If the flat cache don't have default register map, when do regcache_sync operation, the non volatile and writable registers will be synchronised to 0. FSL_SAI_TDR reigster will be written a 0 and cause channel swap. So add default register map for flat cache, and such register will not be written. Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index a4435f5..987fc54 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -608,6 +608,22 @@ static const struct snd_soc_component_driver fsl_component = { .name = "fsl-sai", }; +static struct reg_default fsl_sai_reg_defaults[] = { + {FSL_SAI_TCR1, 0}, + {FSL_SAI_TCR2, 0}, + {FSL_SAI_TCR3, 0}, + {FSL_SAI_TCR4, 0}, + {FSL_SAI_TCR5, 0}, + {FSL_SAI_TDR, 0}, + {FSL_SAI_TMR, 0}, + {FSL_SAI_RCR1, 0}, + {FSL_SAI_RCR2, 0}, + {FSL_SAI_RCR3, 0}, + {FSL_SAI_RCR4, 0}, + {FSL_SAI_RCR5, 0}, + {FSL_SAI_RMR, 0}, +}; + static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -641,13 +657,11 @@ static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg) case FSL_SAI_RCSR: case FSL_SAI_TFR: case FSL_SAI_RFR: - case FSL_SAI_TDR: case FSL_SAI_RDR: return true; default: return false; } - } static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg) @@ -680,6 +694,8 @@ static const struct regmap_config fsl_sai_regmap_config = { .val_bits = 32, .max_register = FSL_SAI_RMR, + .reg_defaults = fsl_sai_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(fsl_sai_reg_defaults), .readable_reg = fsl_sai_readable_reg, .volatile_reg = fsl_sai_volatile_reg, .writeable_reg = fsl_sai_writeable_reg, -- cgit v0.10.2 From 9f1206dc76a726e1c7b0e2583345c29fd1e75286 Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Mon, 26 Oct 2015 15:19:04 +0800 Subject: ASoC: fsl_spdif: STL and STR registers are non volatile STL and STR registers are writable and not readable. So they are non volatile. Remove them from volatile list, and add default register value for them. Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 3d59bb6..28a8823 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1006,12 +1006,14 @@ static const struct snd_soc_component_driver fsl_spdif_component = { /* FSL SPDIF REGMAP */ static const struct reg_default fsl_spdif_reg_defaults[] = { - {0x0, 0x00000400}, - {0x4, 0x00000000}, - {0xc, 0x00000000}, - {0x34, 0x00000000}, - {0x38, 0x00000000}, - {0x50, 0x00020f00}, + {REG_SPDIF_SCR, 0x00000400}, + {REG_SPDIF_SRCD, 0x00000000}, + {REG_SPDIF_SIE, 0x00000000}, + {REG_SPDIF_STL, 0x00000000}, + {REG_SPDIF_STR, 0x00000000}, + {REG_SPDIF_STCSCH, 0x00000000}, + {REG_SPDIF_STCSCL, 0x00000000}, + {REG_SPDIF_STC, 0x00020f00}, }; static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg) @@ -1049,8 +1051,6 @@ static bool fsl_spdif_volatile_reg(struct device *dev, unsigned int reg) case REG_SPDIF_SRCSL: case REG_SPDIF_SRU: case REG_SPDIF_SRQ: - case REG_SPDIF_STL: - case REG_SPDIF_STR: case REG_SPDIF_SRFM: return true; default: -- cgit v0.10.2 From f4faa29e5d134fdff00403936ab10fea7683913e Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Mon, 26 Oct 2015 15:19:05 +0800 Subject: ASoC: fsl_ssi: using macro for default register map using macro for default register map Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 95d2392..674abf7 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -113,17 +113,17 @@ struct fsl_ssi_rxtx_reg_val { }; static const struct reg_default fsl_ssi_reg_defaults[] = { - {0x10, 0x00000000}, - {0x18, 0x00003003}, - {0x1c, 0x00000200}, - {0x20, 0x00000200}, - {0x24, 0x00040000}, - {0x28, 0x00040000}, - {0x38, 0x00000000}, - {0x48, 0x00000000}, - {0x4c, 0x00000000}, - {0x54, 0x00000000}, - {0x58, 0x00000000}, + {CCSR_SSI_SCR, 0x00000000}, + {CCSR_SSI_SIER, 0x00003003}, + {CCSR_SSI_STCR, 0x00000200}, + {CCSR_SSI_SRCR, 0x00000200}, + {CCSR_SSI_STCCR, 0x00040000}, + {CCSR_SSI_SRCCR, 0x00040000}, + {CCSR_SSI_SACNT, 0x00000000}, + {CCSR_SSI_STMSK, 0x00000000}, + {CCSR_SSI_SRMSK, 0x00000000}, + {CCSR_SSI_SACCEN, 0x00000000}, + {CCSR_SSI_SACCDIS, 0x00000000}, }; static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg) -- cgit v0.10.2 From 9cc58712358cbfe51248ef369fc50671149b60fc Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Mon, 9 Nov 2015 19:02:29 +0800 Subject: ASoC: fsl-sai: don't set bclk for Tx/Rx Synchronous with another SAI mode In fsl_sai_set_bclk function, we should not set bclk for Tx/Rx Synchronous with another SAI mode. Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index a4435f5..7e421a9 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -354,13 +354,25 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) return -EINVAL; } - if ((tx && sai->synchronous[TX]) || (!tx && !sai->synchronous[RX])) { + /* + * 1) For Asynchronous mode, we must set RCR2 register for capture, and + * set TCR2 register for playback. + * 2) For Tx sync with Rx clock, we must set RCR2 register for playback + * and capture. + * 3) For Rx sync with Tx clock, we must set TCR2 register for playback + * and capture. + * 4) For Tx and Rx are both Synchronous with another SAI, we just + * ignore it. + */ + if ((sai->synchronous[TX] && !sai->synchronous[RX]) || + (!tx && !sai->synchronous[RX])) { regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_DIV_MASK, savediv - 1); - } else { + } else if ((sai->synchronous[RX] && !sai->synchronous[TX]) || + (tx && !sai->synchronous[TX])) { regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); -- cgit v0.10.2 From 51659ca069ce5bdf20675a7967a39ef8419e87f2 Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Mon, 9 Nov 2015 19:03:13 +0800 Subject: ASoC: fsl-sai: set xCR4/xCR5/xMR for SAI master mode For SAI master mode, when Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will generate bclk and frame clock for Tx(Rx), we should set RCR4(TCR4), RCR5(TCR5) and RMR(TMR) for playback(capture), or there will be sync error sometimes. Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 7e421a9..520dbad 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -427,6 +427,35 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, val_cr4 |= FSL_SAI_CR4_FRSZ(channels); + /* + * For SAI master mode, when Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will + * generate bclk and frame clock for Tx(Rx), we should set RCR4(TCR4), + * RCR5(TCR5) and RMR(TMR) for playback(capture), or there will be sync + * error. + */ + + if (!sai->is_slave_mode) { + if (!sai->synchronous[TX] && sai->synchronous[RX] && !tx) { + regmap_update_bits(sai->regmap, FSL_SAI_TCR4, + FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK, + val_cr4); + regmap_update_bits(sai->regmap, FSL_SAI_TCR5, + FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK | + FSL_SAI_CR5_FBT_MASK, val_cr5); + regmap_write(sai->regmap, FSL_SAI_TMR, + ~0UL - ((1 << channels) - 1)); + } else if (!sai->synchronous[RX] && sai->synchronous[TX] && tx) { + regmap_update_bits(sai->regmap, FSL_SAI_RCR4, + FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK, + val_cr4); + regmap_update_bits(sai->regmap, FSL_SAI_RCR5, + FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK | + FSL_SAI_CR5_FBT_MASK, val_cr5); + regmap_write(sai->regmap, FSL_SAI_RMR, + ~0UL - ((1 << channels) - 1)); + } + } + regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx), FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK, val_cr4); -- cgit v0.10.2 From c1df29648f1e3ffb8bac38e27a22b50f5c019adf Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Tue, 24 Nov 2015 15:31:54 +0800 Subject: ASoC: fsl_sai: add tdm slots operation support Add tdm slots operation support. If tdm slots and slot width have been configured in machine driver, we should use these values. Otherwise, using relevant channels and word length to set slots and slot width. SAI will generate BCLK depends on sample rate, slots and slot width. And there may be unused BCLK cycles before each LRCLK transition. Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 520dbad..43ba5dc 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -126,6 +126,17 @@ out: return IRQ_HANDLED; } +static int fsl_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, + u32 rx_mask, int slots, int slot_width) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + + sai->slots = slots; + sai->slot_width = slot_width; + + return 0; +} + static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int fsl_dir) { @@ -395,11 +406,19 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, unsigned int channels = params_channels(params); u32 word_width = snd_pcm_format_width(params_format(params)); u32 val_cr4 = 0, val_cr5 = 0; + u32 slots = (channels == 1) ? 2 : channels; + u32 slot_width = word_width; int ret; + if (sai->slots) + slots = sai->slots; + + if (sai->slot_width) + slot_width = sai->slot_width; + if (!sai->is_slave_mode) { ret = fsl_sai_set_bclk(cpu_dai, tx, - 2 * word_width * params_rate(params)); + slots * slot_width * params_rate(params)); if (ret) return ret; @@ -411,21 +430,20 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, sai->mclk_streams |= BIT(substream->stream); } - } if (!sai->is_dsp_mode) - val_cr4 |= FSL_SAI_CR4_SYWD(word_width); + val_cr4 |= FSL_SAI_CR4_SYWD(slot_width); - val_cr5 |= FSL_SAI_CR5_WNW(word_width); - val_cr5 |= FSL_SAI_CR5_W0W(word_width); + val_cr5 |= FSL_SAI_CR5_WNW(slot_width); + val_cr5 |= FSL_SAI_CR5_W0W(slot_width); if (sai->is_lsb_first) val_cr5 |= FSL_SAI_CR5_FBT(0); else val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1); - val_cr4 |= FSL_SAI_CR4_FRSZ(channels); + val_cr4 |= FSL_SAI_CR4_FRSZ(slots); /* * For SAI master mode, when Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will @@ -591,6 +609,7 @@ static void fsl_sai_shutdown(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { .set_sysclk = fsl_sai_set_dai_sysclk, .set_fmt = fsl_sai_set_dai_fmt, + .set_tdm_slot = fsl_sai_set_dai_tdm_slot, .hw_params = fsl_sai_hw_params, .hw_free = fsl_sai_hw_free, .trigger = fsl_sai_trigger, diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index b95fbc3..d9ed7be 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -143,6 +143,9 @@ struct fsl_sai { unsigned int mclk_id[2]; unsigned int mclk_streams; + unsigned int slots; + unsigned int slot_width; + struct snd_dmaengine_dai_dma_data dma_params_rx; struct snd_dmaengine_dai_dma_data dma_params_tx; }; -- cgit v0.10.2 From 4ca730436a676afebbe6b77d65b5b4c4d7d38b9c Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Tue, 24 Nov 2015 15:32:09 +0800 Subject: ASoC: fsl: using params_width function to simplify code using params_width function to simplify code. Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 9f087d4..6d06366 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -447,7 +447,7 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai); - int width = snd_pcm_format_width(params_format(params)); + int width = params_width(params); struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_asrc_pair *pair = runtime->private_data; unsigned int channels = params_channels(params); diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 504e731..45d4319 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -510,7 +510,7 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream, { struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; - u32 width = snd_pcm_format_width(params_format(params)); + u32 width = params_width(params); u32 channels = params_channels(params); u32 pins = DIV_ROUND_UP(channels, esai_priv->slots); u32 slot_width = width; diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index dc0cc65..3da2783 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -404,7 +404,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; unsigned int channels = params_channels(params); - u32 word_width = snd_pcm_format_width(params_format(params)); + u32 word_width = params_width(params); u32 val_cr4 = 0, val_cr5 = 0; u32 slots = (channels == 1) ? 2 : channels; u32 slot_width = word_width; diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 674abf7..e3abad5 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -767,8 +767,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); struct regmap *regs = ssi_private->regs; unsigned int channels = params_channels(hw_params); - unsigned int sample_size = - snd_pcm_format_width(params_format(hw_params)); + unsigned int sample_size = params_width(hw_params); u32 wl = CCSR_SSI_SxCCR_WL(sample_size); int ret; u32 scr_val; -- cgit v0.10.2 From a2a4d6049aa18c0e105d9b53e3236cb50ea5bfa1 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 24 Nov 2015 17:19:32 +0800 Subject: ASoC: fsl_esai: spba clock is needed by esai device ESAI need to enable the spba clock, when sdma is using share peripheral script. In this case, there is two spba master port is used, if don't enable the clock, the spba bus will have arbitration issue, which may cause read/write wrong data from/to ESAI registers. Signed-off-by: Shengjiu Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/fsl,esai.txt b/Documentation/devicetree/bindings/sound/fsl,esai.txt index d3b6b5f..cd3ee5d 100644 --- a/Documentation/devicetree/bindings/sound/fsl,esai.txt +++ b/Documentation/devicetree/bindings/sound/fsl,esai.txt @@ -27,6 +27,11 @@ Required properties: derive HCK, SCK and FS. "fsys" The system clock derived from ahb clock used to derive HCK, SCK and FS. + "spba" The spba clock is required when ESAI is placed as a + bus slave of the Shared Peripheral Bus and when two + or more bus masters (CPU, DMA or DSP) try to access + it. This property is optional depending on the SoC + design. - fsl,fifo-depth : The number of elements in the transmit and receive FIFOs. This number is the maximum allowed value for diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 59f234e..6746f76 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -35,6 +35,7 @@ * @coreclk: clock source to access register * @extalclk: esai clock source to derive HCK, SCK and FS * @fsysclk: system clock source to derive HCK, SCK and FS + * @spbaclk: SPBA clock (optional, depending on SoC design) * @fifo_depth: depth of tx/rx FIFO * @slot_width: width of each DAI slot * @slots: number of slots @@ -54,6 +55,7 @@ struct fsl_esai { struct clk *coreclk; struct clk *extalclk; struct clk *fsysclk; + struct clk *spbaclk; u32 fifo_depth; u32 slot_width; u32 slots; @@ -469,6 +471,11 @@ static int fsl_esai_startup(struct snd_pcm_substream *substream, ret = clk_prepare_enable(esai_priv->coreclk); if (ret) return ret; + if (!IS_ERR(esai_priv->spbaclk)) { + ret = clk_prepare_enable(esai_priv->spbaclk); + if (ret) + goto err_spbaclk; + } if (!IS_ERR(esai_priv->extalclk)) { ret = clk_prepare_enable(esai_priv->extalclk); if (ret) @@ -499,6 +506,9 @@ err_fsysclk: if (!IS_ERR(esai_priv->extalclk)) clk_disable_unprepare(esai_priv->extalclk); err_extalck: + if (!IS_ERR(esai_priv->spbaclk)) + clk_disable_unprepare(esai_priv->spbaclk); +err_spbaclk: clk_disable_unprepare(esai_priv->coreclk); return ret; @@ -564,6 +574,8 @@ static void fsl_esai_shutdown(struct snd_pcm_substream *substream, clk_disable_unprepare(esai_priv->fsysclk); if (!IS_ERR(esai_priv->extalclk)) clk_disable_unprepare(esai_priv->extalclk); + if (!IS_ERR(esai_priv->spbaclk)) + clk_disable_unprepare(esai_priv->spbaclk); clk_disable_unprepare(esai_priv->coreclk); } @@ -819,6 +831,11 @@ static int fsl_esai_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "failed to get fsys clock: %ld\n", PTR_ERR(esai_priv->fsysclk)); + esai_priv->spbaclk = devm_clk_get(&pdev->dev, "spba"); + if (IS_ERR(esai_priv->spbaclk)) + dev_warn(&pdev->dev, "failed to get spba clock: %ld\n", + PTR_ERR(esai_priv->spbaclk)); + irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); -- cgit v0.10.2 From 13b8a97a760eee021558dc48fd65e77e8a362909 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 24 Nov 2015 17:19:34 +0800 Subject: ASoC: fsl_asrc: spba clock is needed by asrc device ASRC need to enable the spba clock, when sdma is using share peripheral script. In this case, there is two spba master port is used, if don't enable the clock, the spba bus will have arbitration issue, which may cause read/write wrong data from/to ASRC registers Signed-off-by: Shengjiu Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/fsl,asrc.txt b/Documentation/devicetree/bindings/sound/fsl,asrc.txt index b93362a..3e26a94 100644 --- a/Documentation/devicetree/bindings/sound/fsl,asrc.txt +++ b/Documentation/devicetree/bindings/sound/fsl,asrc.txt @@ -25,6 +25,11 @@ Required properties: "mem" Peripheral access clock to access registers. "ipg" Peripheral clock to driver module. "asrck_<0-f>" Clock sources for input and output clock. + "spba" The spba clock is required when ASRC is placed as a + bus slave of the Shared Peripheral Bus and when two + or more bus masters (CPU, DMA or DSP) try to access + it. This property is optional depending on the SoC + design. - big-endian : If this property is absent, the little endian mode will be in use as default. Otherwise, the big endian diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 9f087d4..cf382475 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -859,6 +859,10 @@ static int fsl_asrc_probe(struct platform_device *pdev) return PTR_ERR(asrc_priv->ipg_clk); } + asrc_priv->spba_clk = devm_clk_get(&pdev->dev, "spba"); + if (IS_ERR(asrc_priv->spba_clk)) + dev_warn(&pdev->dev, "failed to get spba clock\n"); + for (i = 0; i < ASRC_CLK_MAX_NUM; i++) { sprintf(tmp, "asrck_%x", i); asrc_priv->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp); @@ -939,6 +943,11 @@ static int fsl_asrc_runtime_resume(struct device *dev) ret = clk_prepare_enable(asrc_priv->ipg_clk); if (ret) goto disable_mem_clk; + if (!IS_ERR(asrc_priv->spba_clk)) { + ret = clk_prepare_enable(asrc_priv->spba_clk); + if (ret) + goto disable_ipg_clk; + } for (i = 0; i < ASRC_CLK_MAX_NUM; i++) { ret = clk_prepare_enable(asrc_priv->asrck_clk[i]); if (ret) @@ -950,6 +959,9 @@ static int fsl_asrc_runtime_resume(struct device *dev) disable_asrck_clk: for (i--; i >= 0; i--) clk_disable_unprepare(asrc_priv->asrck_clk[i]); + if (!IS_ERR(asrc_priv->spba_clk)) + clk_disable_unprepare(asrc_priv->spba_clk); +disable_ipg_clk: clk_disable_unprepare(asrc_priv->ipg_clk); disable_mem_clk: clk_disable_unprepare(asrc_priv->mem_clk); @@ -963,6 +975,8 @@ static int fsl_asrc_runtime_suspend(struct device *dev) for (i = 0; i < ASRC_CLK_MAX_NUM; i++) clk_disable_unprepare(asrc_priv->asrck_clk[i]); + if (!IS_ERR(asrc_priv->spba_clk)) + clk_disable_unprepare(asrc_priv->spba_clk); clk_disable_unprepare(asrc_priv->ipg_clk); clk_disable_unprepare(asrc_priv->mem_clk); diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h index 4aed63c..68802cd 100644 --- a/sound/soc/fsl/fsl_asrc.h +++ b/sound/soc/fsl/fsl_asrc.h @@ -426,6 +426,7 @@ struct fsl_asrc_pair { * @paddr: physical address to the base address of registers * @mem_clk: clock source to access register * @ipg_clk: clock source to drive peripheral + * @spba_clk: SPBA clock (optional, depending on SoC design) * @asrck_clk: clock sources to driver ASRC internal logic * @lock: spin lock for resource protection * @pair: pair pointers @@ -442,6 +443,7 @@ struct fsl_asrc { unsigned long paddr; struct clk *mem_clk; struct clk *ipg_clk; + struct clk *spba_clk; struct clk *asrck_clk[ASRC_CLK_MAX_NUM]; spinlock_t lock; -- cgit v0.10.2 From f48303122d2fd94b719e546cf8a39d412c7eee69 Mon Sep 17 00:00:00 2001 From: Maruthi Srinivas Bayyavarapu Date: Fri, 4 Dec 2015 18:40:31 -0500 Subject: ASoC: dwc: add runtime suspend/resume functionality When DW controller is in master mode, it can disable/enable clock during the device runtime suspend/resume sequence. Signed-off-by: Maruthi Bayyavarapu Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher Signed-off-by: Mark Brown diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 6e6a70c..3d7754c 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -394,6 +395,23 @@ static const struct snd_soc_component_driver dw_i2s_component = { }; #ifdef CONFIG_PM +static int dw_i2s_runtime_suspend(struct device *dev) +{ + struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev); + + if (dw_dev->capability & DW_I2S_MASTER) + clk_disable(dw_dev->clk); + return 0; +} + +static int dw_i2s_runtime_resume(struct device *dev) +{ + struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev); + + if (dw_dev->capability & DW_I2S_MASTER) + clk_enable(dw_dev->clk); + return 0; +} static int dw_i2s_suspend(struct snd_soc_dai *dai) { @@ -649,7 +667,7 @@ static int dw_i2s_probe(struct platform_device *pdev) goto err_clk_disable; } } - + pm_runtime_enable(&pdev->dev); return 0; err_clk_disable: @@ -665,6 +683,7 @@ static int dw_i2s_remove(struct platform_device *pdev) if (dev->capability & DW_I2S_MASTER) clk_disable_unprepare(dev->clk); + pm_runtime_disable(&pdev->dev); return 0; } @@ -677,12 +696,17 @@ static const struct of_device_id dw_i2s_of_match[] = { MODULE_DEVICE_TABLE(of, dw_i2s_of_match); #endif +static const struct dev_pm_ops dwc_pm_ops = { + SET_RUNTIME_PM_OPS(dw_i2s_runtime_suspend, dw_i2s_runtime_resume, NULL) +}; + static struct platform_driver dw_i2s_driver = { .probe = dw_i2s_probe, .remove = dw_i2s_remove, .driver = { .name = "designware-i2s", .of_match_table = of_match_ptr(dw_i2s_of_match), + .pm = &dwc_pm_ops, }, }; -- cgit v0.10.2 From e164835a0270cc01c93794536027cc70cd00d0ff Mon Sep 17 00:00:00 2001 From: Maruthi Srinivas Bayyavarapu Date: Fri, 4 Dec 2015 18:40:32 -0500 Subject: ASoC: dwc: add quirk for different register offset DWC in ACP 2.x IP has different offsets for I2S_COMP_PARAM_* registers. Added a quirk to support the same. Signed-off-by: Maruthi Bayyavarapu Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher Signed-off-by: Mark Brown diff --git a/include/sound/designware_i2s.h b/include/sound/designware_i2s.h index 8966ba7..e0bb458 100644 --- a/include/sound/designware_i2s.h +++ b/include/sound/designware_i2s.h @@ -45,6 +45,11 @@ struct i2s_platform_data { u32 snd_fmts; u32 snd_rates; + #define DW_I2S_QUIRK_COMP_REG_OFFSET (1 << 0) + unsigned int quirks; + unsigned int i2s_reg_comp1; + unsigned int i2s_reg_comp2; + void *play_dma_data; void *capture_dma_data; bool (*filter)(struct dma_chan *chan, void *slave); diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 3d7754c..940c881 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -94,6 +94,9 @@ struct dw_i2s_dev { struct clk *clk; int active; unsigned int capability; + unsigned int quirks; + unsigned int i2s_reg_comp1; + unsigned int i2s_reg_comp2; struct device *dev; /* data related to DMA transfers b/w i2s and DMAC */ @@ -477,8 +480,8 @@ static int dw_configure_dai(struct dw_i2s_dev *dev, * Read component parameter registers to extract * the I2S block's configuration. */ - u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); - u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2); + u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1); + u32 comp2 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp2); u32 idx; if (COMP1_TX_ENABLED(comp1)) { @@ -521,7 +524,7 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, struct resource *res, const struct i2s_platform_data *pdata) { - u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); + u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1); u32 idx = COMP1_APB_DATA_WIDTH(comp1); int ret; @@ -625,6 +628,14 @@ static int dw_i2s_probe(struct platform_device *pdev) if (pdata) { dev->capability = pdata->cap; clk_id = NULL; + dev->quirks = pdata->quirks; + if (dev->quirks & DW_I2S_QUIRK_COMP_REG_OFFSET) { + dev->i2s_reg_comp1 = pdata->i2s_reg_comp1; + dev->i2s_reg_comp2 = pdata->i2s_reg_comp2; + } else { + dev->i2s_reg_comp1 = I2S_COMP_PARAM_1; + dev->i2s_reg_comp2 = I2S_COMP_PARAM_2; + } ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); } else { clk_id = "i2sclk"; -- cgit v0.10.2 From 0032e9dbc5d8add10345ccda48e3803bb7cfd650 Mon Sep 17 00:00:00 2001 From: Maruthi Srinivas Bayyavarapu Date: Fri, 4 Dec 2015 18:40:33 -0500 Subject: ASoC: dwc: reconfigure dwc in 'resume' from 'suspend' DWC IP can be powered off during system suspend in some platforms. After system is resumed, dwc needs to be programmed again to continue audio use case. Signed-off-by: Maruthi Bayyavarapu Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher Signed-off-by: Mark Brown diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 940c881..825a1f4 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -98,6 +98,8 @@ struct dw_i2s_dev { unsigned int i2s_reg_comp1; unsigned int i2s_reg_comp2; struct device *dev; + u32 ccr; + u32 xfer_resolution; /* data related to DMA transfers b/w i2s and DMAC */ union dw_i2s_snd_dma_data play_dma_data; @@ -217,31 +219,58 @@ static int dw_i2s_startup(struct snd_pcm_substream *substream, return 0; } +static void dw_i2s_config(struct dw_i2s_dev *dev, int stream) +{ + u32 ch_reg, irq; + struct i2s_clk_config_data *config = &dev->config; + + + i2s_disable_channels(dev, stream); + + for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + i2s_write_reg(dev->i2s_base, TCR(ch_reg), + dev->xfer_resolution); + i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02); + irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); + i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30); + i2s_write_reg(dev->i2s_base, TER(ch_reg), 1); + } else { + i2s_write_reg(dev->i2s_base, RCR(ch_reg), + dev->xfer_resolution); + i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07); + irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); + i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03); + i2s_write_reg(dev->i2s_base, RER(ch_reg), 1); + } + + } +} + static int dw_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); struct i2s_clk_config_data *config = &dev->config; - u32 ccr, xfer_resolution, ch_reg, irq; int ret; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: config->data_width = 16; - ccr = 0x00; - xfer_resolution = 0x02; + dev->ccr = 0x00; + dev->xfer_resolution = 0x02; break; case SNDRV_PCM_FORMAT_S24_LE: config->data_width = 24; - ccr = 0x08; - xfer_resolution = 0x04; + dev->ccr = 0x08; + dev->xfer_resolution = 0x04; break; case SNDRV_PCM_FORMAT_S32_LE: config->data_width = 32; - ccr = 0x10; - xfer_resolution = 0x05; + dev->ccr = 0x10; + dev->xfer_resolution = 0x05; break; default: @@ -262,27 +291,9 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - i2s_disable_channels(dev, substream->stream); + dw_i2s_config(dev, substream->stream); - for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) { - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - i2s_write_reg(dev->i2s_base, TCR(ch_reg), - xfer_resolution); - i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02); - irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); - i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30); - i2s_write_reg(dev->i2s_base, TER(ch_reg), 1); - } else { - i2s_write_reg(dev->i2s_base, RCR(ch_reg), - xfer_resolution); - i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07); - irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); - i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03); - i2s_write_reg(dev->i2s_base, RER(ch_reg), 1); - } - } - - i2s_write_reg(dev->i2s_base, CCR, ccr); + i2s_write_reg(dev->i2s_base, CCR, dev->ccr); config->sample_rate = params_rate(params); @@ -431,6 +442,11 @@ static int dw_i2s_resume(struct snd_soc_dai *dai) if (dev->capability & DW_I2S_MASTER) clk_enable(dev->clk); + + if (dai->playback_active) + dw_i2s_config(dev, SNDRV_PCM_STREAM_PLAYBACK); + if (dai->capture_active) + dw_i2s_config(dev, SNDRV_PCM_STREAM_CAPTURE); return 0; } -- cgit v0.10.2 From 34e684fa04fadd513e028fa48123f357deac77e8 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 8 Dec 2015 16:35:51 +0100 Subject: ASoC: fsl: use correct format string for dma_addr_t We get a warning for the imx-pcm-fiq driver when CONFIG_LPAE is enabled on ARM, because dma_addr_t is 64-bit then: sound/soc/fsl/imx-pcm-fiq.c: In function 'snd_imx_pcm_mmap': sound/soc/fsl/imx-pcm-fiq.c:223:107: warning: format '%x' expects argument of type 'unsigned int', but argument 6 has type 'dma_addr_t {aka long long unsigned int}' [-Wformat=] This changes the printk to use the correct format string for printing a dma_addr_t. Signed-off-by: Arnd Bergmann Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index 7abf6a0..49d7513 100644 --- a/sound/soc/fsl/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -220,9 +220,9 @@ static int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, ret = dma_mmap_writecombine(substream->pcm->card->dev, vma, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); - pr_debug("%s: ret: %d %p 0x%08x 0x%08x\n", __func__, ret, + pr_debug("%s: ret: %d %p %pad 0x%08x\n", __func__, ret, runtime->dma_area, - runtime->dma_addr, + &runtime->dma_addr, runtime->dma_bytes); return ret; } -- cgit v0.10.2 From 906c7d690c3b80e4321178c083db8c14afb56bf8 Mon Sep 17 00:00:00 2001 From: PC Liao Date: Fri, 11 Dec 2015 11:33:51 +0800 Subject: ASoC: dpcm: Apply symmetry for DPCM DPCM does not fully support symmetry attributes. soc_pcm_apply_symmetry() is skipped in soc_pcm_open() for DPCM, without being applied elsewhere. So HW parameters cannot be correctly limited, and user space can do playback/capture at different rates while HW actually does not support it. soc_pcm_params_symmetry() will return error and the second stream stops. This patch adds soc_pcm_apply_symmetry() for FE, BE, and codec DAIs in DPCM path that was skipped in soc_pcm_open(). Signed-off-by: PC Liao Signed-off-by: Koro Chen Acked-by: Liam Girdwood Signed-off-by: Mark Brown diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index c482322..37de8af 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1616,6 +1616,56 @@ static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe, snd_pcm_stream_unlock_irq(substream); } +static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream, + int stream) +{ + struct snd_soc_dpcm *dpcm; + struct snd_soc_pcm_runtime *fe = fe_substream->private_data; + struct snd_soc_dai *fe_cpu_dai = fe->cpu_dai; + int err; + + /* apply symmetry for FE */ + if (soc_pcm_has_symmetry(fe_substream)) + fe_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; + + /* Symmetry only applies if we've got an active stream. */ + if (fe_cpu_dai->active) { + err = soc_pcm_apply_symmetry(fe_substream, fe_cpu_dai); + if (err < 0) + return err; + } + + /* apply symmetry for BE */ + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_pcm_substream *be_substream = + snd_soc_dpcm_get_substream(be, stream); + struct snd_soc_pcm_runtime *rtd = be_substream->private_data; + int i; + + if (soc_pcm_has_symmetry(be_substream)) + be_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; + + /* Symmetry only applies if we've got an active stream. */ + if (rtd->cpu_dai->active) { + err = soc_pcm_apply_symmetry(be_substream, rtd->cpu_dai); + if (err < 0) + return err; + } + + for (i = 0; i < rtd->num_codecs; i++) { + if (rtd->codec_dais[i]->active) { + err = soc_pcm_apply_symmetry(be_substream, + rtd->codec_dais[i]); + if (err < 0) + return err; + } + } + } + + return 0; +} + static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) { struct snd_soc_pcm_runtime *fe = fe_substream->private_data; @@ -1644,6 +1694,13 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) dpcm_set_fe_runtime(fe_substream); snd_pcm_limit_hw_rates(runtime); + ret = dpcm_apply_symmetry(fe_substream, stream); + if (ret < 0) { + dev_err(fe->dev, "ASoC: failed to apply dpcm symmetry %d\n", + ret); + goto unwind; + } + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); return 0; -- cgit v0.10.2 From fff6e03c7b659bfa2fa001b0ede71e4830a84b56 Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Fri, 18 Dec 2015 17:00:09 +0800 Subject: ASoC: fsl_asrc: add support for 8-30kHz output sample rate Add 8kHz, 11.025kHz, 16kHz, 22.05kHz output sample rate support. According referance menual, "Limited support for the case when output sampling rates is between 8kHz and 30kHz. The limitation is the supported ratio (Fsin/Fsout) range as between 1/24 to 8." Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index cf382475..7b81148 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -31,21 +31,21 @@ dev_dbg(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__) /* Sample rates are aligned with that defined in pcm.h file */ -static const u8 process_option[][8][2] = { - /* 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176kHz 192kHz */ - {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 5512Hz */ - {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 8kHz */ - {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 11025Hz */ - {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 16kHz */ - {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 22050Hz */ - {{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},}, /* 32kHz */ - {{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 44.1kHz */ - {{0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 48kHz */ - {{1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},}, /* 64kHz */ - {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 88.2kHz */ - {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 96kHz */ - {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 176kHz */ - {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 192kHz */ +static const u8 process_option[][12][2] = { + /* 8kHz 11.025kHz 16kHz 22.05kHz 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176kHz 192kHz */ + {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 5512Hz */ + {{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 8kHz */ + {{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 11025Hz */ + {{1, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 16kHz */ + {{1, 2}, {1, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 22050Hz */ + {{1, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},}, /* 32kHz */ + {{2, 2}, {2, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 44.1kHz */ + {{2, 2}, {2, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 48kHz */ + {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},}, /* 64kHz */ + {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 88.2kHz */ + {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 96kHz */ + {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 176kHz */ + {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 192kHz */ }; /* Corresponding to process_option */ @@ -55,7 +55,7 @@ static int supported_input_rate[] = { }; static int supported_asrc_rate[] = { - 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000, + 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000, }; /** @@ -286,6 +286,13 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) return -EINVAL; } + if ((outrate > 8000 && outrate < 30000) && + (outrate/inrate > 24 || inrate/outrate > 8)) { + pair_err("exceed supported ratio range [1/24, 8] for \ + inrate/outrate: %d/%d\n", inrate, outrate); + return -EINVAL; + } + /* Validate input and output clock sources */ clk_index[IN] = clk_map[IN][config->inclk]; clk_index[OUT] = clk_map[OUT][config->outclk]; -- cgit v0.10.2 From 25e5ef974c33f1e4a07a68bf830e6493ee6dab11 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sun, 20 Dec 2015 21:34:29 +0100 Subject: ASoC: fsl-asoc-card: use different route map for AC'97 mode fsl_ssi uses different stream names ("AC97 Playback" / "AC97 Capture") in AC'97 mode so in this case fsl-asoc-card route map should also be using them. Signed-off-by: Maciej S. Szmigiero Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 1b05d1c..6fb3aed 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -107,6 +107,13 @@ static const struct snd_soc_dapm_route audio_map[] = { {"CPU-Capture", NULL, "Capture"}, }; +static const struct snd_soc_dapm_route audio_map_ac97[] = { + {"AC97 Playback", NULL, "ASRC-Playback"}, + {"Playback", NULL, "AC97 Playback"}, + {"ASRC-Capture", NULL, "AC97 Capture"}, + {"AC97 Capture", NULL, "Capture"}, +}; + /* Add all possible widgets into here without being redundant */ static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = { SND_SOC_DAPM_LINE("Line Out Jack", NULL), @@ -574,7 +581,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->card.dev = &pdev->dev; priv->card.name = priv->name; priv->card.dai_link = priv->dai_link; - priv->card.dapm_routes = audio_map; + priv->card.dapm_routes = fsl_asoc_card_is_ac97(priv) ? + audio_map_ac97 : audio_map; priv->card.late_probe = fsl_asoc_card_late_probe; priv->card.num_dapm_routes = ARRAY_SIZE(audio_map); priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets; -- cgit v0.10.2