[alsa-devel] [PATCH v2 00/16] ASoC: fsl_ssi: Clean up - program flow level
==Change log== v2 * Reworked the series by taking suggestions from Maciej + Added PATCH-01 to keep all ssi->i2s_net updated + Replaced bool tx with bool dir in PATCH-03 and PATCH-06 + Moved all initial register configurations from dai probe() to platform probe() so as to let AC97 CODEC successfully probe. * Added Tested-by from Caleb for TDM test cases.
==Background== The fsl_ssi driver was designed for PPC originally and then it has been updated to support different modes for i.MX Series, including SDMA, I2S Master mode, AC97 and older i.MXs with FIQ, by different contributors for different use cases in different coding styles.
Additionally, in order to fix/work-around hardware bugs and design flaws, the driver made a lot of compromise so now its program flow looks very complicated and it's getting hard to maintain or update.
So I am going to clean up the driver on both coding style level and program flow level.
==Introduction== This series of patches is the second set to clean up fsl_ssi driver in the program flow level. Any patch here may impact a fundamental test case like playback or record.
==Verification== This series of patches require fully tested. I have done such tests on i.MX6SoloX with WM8962 using imx_v6_v7_defconfig as: - Playback via I2S Master and Slave mode - Record via I2S Master and Slave mode - Simultaneous playback and record via I2S Master and Slave mode - Background playback with foreground record (starting at different time) via I2S Master and Slave mode - Background record with foreground playback (starting at different time) via I2S Master and Slave mode * All tests above by hacking offline_config to true in imx51.
Caleb has tested v1 with TDM lookback tests on i.MX6.
Example of uncovered tests: AC97, PowerPC and FIQ.
Nicolin Chen (16): ASoC: fsl_ssi: Keep ssi->i2s_net updated ASoC: fsl_ssi: Clean up set_dai_tdm_slot() ASoC: fsl_ssi: Maintain a mask of active streams ASoC: fsl_ssi: Rename fsl_ssi_disable_val macro ASoC: fsl_ssi: Clear FIFO directly in fsl_ssi_config() ASoC: fsl_ssi: Clean up helper functions of trigger() ASoC: fsl_ssi: Add DAIFMT define for AC97 ASoC: fsl_ssi: Clean up fsl_ssi_setup_regvals() ASoC: fsl_ssi: Set xFEN0 and xFEN1 together ASoC: fsl_ssi: Use snd_soc_init_dma_data instead ASoC: fsl_ssi: Move one-time configurations to probe() ASoC: fsl_ssi: Setup AC97 in fsl_ssi_hw_init() ASoC: fsl_ssi: Clean up _fsl_ssi_set_dai_fmt() ASoC: fsl_ssi: Remove cpu_dai_drv from fsl_ssi structure ASoC: fsl_ssi: Move DT related code to a separate probe() ASoC: fsl_ssi: Use ssi->streams instead of reading register
sound/soc/fsl/fsl_ssi.c | 740 ++++++++++++++++++++++++------------------------ 1 file changed, 369 insertions(+), 371 deletions(-)
The hw_params() overwrites i2s_net settings for special cases like mono-channel support, however, it doesn't update ssi->i2s_net as set_dai_fmt() does.
This patch removes the local i2s_net variable and directly updates ssi->i2s_net in the hw_params() so that the driver can simply look up the ssi->i2s_net instead of reading the register.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org --- sound/soc/fsl/fsl_ssi.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index aecd00f..c8bd353 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -834,16 +834,16 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, }
if (!fsl_ssi_is_ac97(ssi)) { - u8 i2s_net; /* Normal + Network mode to send 16-bit data in 32-bit frames */ if (fsl_ssi_is_i2s_cbm_cfs(ssi) && sample_size == 16) - i2s_net = SSI_SCR_I2S_MODE_NORMAL | SSI_SCR_NET; - else - i2s_net = ssi->i2s_net; + ssi->i2s_net = SSI_SCR_I2S_MODE_NORMAL | SSI_SCR_NET; + + /* Use Normal mode to send mono data at 1st slot of 2 slots */ + if (channels == 1) + ssi->i2s_net = SSI_SCR_I2S_MODE_NORMAL;
regmap_update_bits(regs, REG_SSI_SCR, - SSI_SCR_I2S_NET_MASK, - channels == 1 ? 0 : i2s_net); + SSI_SCR_I2S_NET_MASK, ssi->i2s_net); }
/* In synchronous mode, the SSI uses STCCR for capture */
The patch
ASoC: fsl_ssi: Keep ssi->i2s_net updated
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From ebf08ae3bc906fc5dd33d02977efa5d4b9831517 Mon Sep 17 00:00:00 2001
From: Nicolin Chen nicoleotsuka@gmail.com Date: Mon, 12 Feb 2018 14:03:10 -0800 Subject: [PATCH] ASoC: fsl_ssi: Keep ssi->i2s_net updated
The hw_params() overwrites i2s_net settings for special cases like mono-channel support, however, it doesn't update ssi->i2s_net as set_dai_fmt() does.
This patch removes the local i2s_net variable and directly updates ssi->i2s_net in the hw_params() so that the driver can simply look up the ssi->i2s_net instead of reading the register.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org Tested-by: Maciej S. Szmigiero mail@maciej.szmigiero.name Reviewed-by: Maciej S. Szmigiero mail@maciej.szmigiero.name Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/fsl/fsl_ssi.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 3c8dd609e42e..d4f1f0d64136 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -838,16 +838,16 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, }
if (!fsl_ssi_is_ac97(ssi)) { - u8 i2s_net; /* Normal + Network mode to send 16-bit data in 32-bit frames */ if (fsl_ssi_is_i2s_cbm_cfs(ssi) && sample_size == 16) - i2s_net = SSI_SCR_I2S_MODE_NORMAL | SSI_SCR_NET; - else - i2s_net = ssi->i2s_net; + ssi->i2s_net = SSI_SCR_I2S_MODE_NORMAL | SSI_SCR_NET; + + /* Use Normal mode to send mono data at 1st slot of 2 slots */ + if (channels == 1) + ssi->i2s_net = SSI_SCR_I2S_MODE_NORMAL;
regmap_update_bits(regs, REG_SSI_SCR, - SSI_SCR_I2S_NET_MASK, - channels == 1 ? 0 : i2s_net); + SSI_SCR_I2S_NET_MASK, ssi->i2s_net); }
/* In synchronous mode, the SSI uses STCCR for capture */
This patch replaces the register read with ssi->i2s_net for simplification. It also removes masking SSIEN from scr value since it's handled later by regmap_update_bits() to set this scr value back.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org --- sound/soc/fsl/fsl_ssi.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index c8bd353..491b660 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1051,9 +1051,7 @@ static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, }
/* The slot number should be >= 2 if using Network mode or I2S mode */ - regmap_read(regs, REG_SSI_SCR, &val); - val &= SSI_SCR_I2S_MODE_MASK | SSI_SCR_NET; - if (val && slots < 2) { + if (ssi->i2s_net && slots < 2) { dev_err(dai->dev, "slot number should be >= 2 in I2S or NET\n"); return -EINVAL; } @@ -1063,9 +1061,8 @@ static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, regmap_update_bits(regs, REG_SSI_SRCCR, SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots));
- /* Save SSIEN bit of the SCR register */ + /* Save the SCR register value */ regmap_read(regs, REG_SSI_SCR, &val); - val &= SSI_SCR_SSIEN; /* Temporarily enable SSI to allow SxMSKs to be configurable */ regmap_update_bits(regs, REG_SSI_SCR, SSI_SCR_SSIEN, SSI_SCR_SSIEN);
Checking TE and RE bits in SCR register doesn't work for AC97 mode which enables SSIEN, TE and RE in the fsl_ssi_setup_ac97() that's called during probe().
So when running into the trigger(), it will always get the result of both TE and RE being enabled already, even if actually there is no active stream.
This patch fixes this issue by adding a variable to log the active streams manually.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org --- sound/soc/fsl/fsl_ssi.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 491b660..aa14a5d 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -201,6 +201,7 @@ struct fsl_ssi_soc_data { * @cpu_dai_drv: CPU DAI driver for this device * * @dai_fmt: DAI configuration this device is currently used with + * @streams: Mask of current active streams: BIT(TX) and BIT(RX) * @i2s_net: I2S and Network mode configurations of SCR register * @use_dma: DMA is used or FIQ with stream filter * @use_dual_fifo: DMA with support for dual FIFO mode @@ -245,6 +246,7 @@ struct fsl_ssi { struct snd_soc_dai_driver cpu_dai_drv;
unsigned int dai_fmt; + u8 streams; u8 i2s_net; bool use_dma; bool use_dual_fifo; @@ -440,15 +442,14 @@ static void fsl_ssi_fifo_clear(struct fsl_ssi *ssi, bool is_rx) static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable, struct fsl_ssi_regvals *vals) { + bool dir = (&ssi->regvals[TX] == vals) ? TX : RX; struct regmap *regs = ssi->regs; struct fsl_ssi_regvals *avals; int nr_active_streams; - u32 scr; int keep_active;
- regmap_read(regs, REG_SSI_SCR, &scr); - - nr_active_streams = !!(scr & SSI_SCR_TE) + !!(scr & SSI_SCR_RE); + nr_active_streams = !!(ssi->streams & BIT(TX)) + + !!(ssi->streams & BIT(RX));
if (nr_active_streams - 1 > 0) keep_active = 1; @@ -470,6 +471,9 @@ static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable, keep_active); /* Safely disable SCR register for the stream */ regmap_update_bits(regs, REG_SSI_SCR, scr, 0); + + /* Log the disabled stream to the mask */ + ssi->streams &= ~BIT(dir); }
/* @@ -545,6 +549,9 @@ static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable, } /* Enable all remaining bits */ regmap_update_bits(regs, REG_SSI_SCR, vals->scr, vals->scr); + + /* Log the enabled stream to the mask */ + ssi->streams |= BIT(dir); } }
On 11.01.2018 07:43, Nicolin Chen wrote:
Checking TE and RE bits in SCR register doesn't work for AC97 mode which enables SSIEN, TE and RE in the fsl_ssi_setup_ac97() that's called during probe().
So when running into the trigger(), it will always get the result of both TE and RE being enabled already, even if actually there is no active stream.
This patch fixes this issue by adding a variable to log the active streams manually.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org
sound/soc/fsl/fsl_ssi.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 491b660..aa14a5d 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -201,6 +201,7 @@ struct fsl_ssi_soc_data {
- @cpu_dai_drv: CPU DAI driver for this device
- @dai_fmt: DAI configuration this device is currently used with
- @streams: Mask of current active streams: BIT(TX) and BIT(RX)
- @i2s_net: I2S and Network mode configurations of SCR register
- @use_dma: DMA is used or FIQ with stream filter
- @use_dual_fifo: DMA with support for dual FIFO mode
@@ -245,6 +246,7 @@ struct fsl_ssi { struct snd_soc_dai_driver cpu_dai_drv;
unsigned int dai_fmt;
- u8 streams; u8 i2s_net; bool use_dma; bool use_dual_fifo;
@@ -440,15 +442,14 @@ static void fsl_ssi_fifo_clear(struct fsl_ssi *ssi, bool is_rx) static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable, struct fsl_ssi_regvals *vals) {
- bool dir = (&ssi->regvals[TX] == vals) ? TX : RX;
Using a bool variable for a bit index (and array index in other parts of code) looks just wrong.
Even a simple int would look better IMHO here (and in patch 5 that rewrites this line a bit).
Maciej
On Sun, Jan 14, 2018 at 11:34:01PM +0100, Maciej S. Szmigiero wrote:
- bool dir = (&ssi->regvals[TX] == vals) ? TX : RX;
Using a bool variable for a bit index (and array index in other parts of code) looks just wrong.
Even a simple int would look better IMHO here (and in patch 5 that rewrites this line a bit).
Will change to int. Thanks
The define of fsl_ssi_disable_val is not so clear as it mixes two steps of calculations together. And those parameter names are also a bit long to read.
Since it just tries to exclude the shared bits from the regvals of current stream while the opposite stream is active, it's better to use something like ssi_excl_shared_bits.
This patch also bisects fsl_ssi_disable_val into two macros of two corresponding steps and then shortens its parameter names. It also updates callers in the fsl_ssi_config() accordingly.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org --- sound/soc/fsl/fsl_ssi.c | 54 ++++++++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 32 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index aa14a5d..f026386 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -417,24 +417,24 @@ static void fsl_ssi_fifo_clear(struct fsl_ssi *ssi, bool is_rx) }
/** - * Calculate the bits that have to be disabled for the current stream that is - * getting disabled. This keeps the bits enabled that are necessary for the - * second stream to work if 'stream_active' is true. + * Exclude bits that are used by the opposite stream * - * Detailed calculation: - * These are the values that need to be active after disabling. For non-active - * second stream, this is 0: - * vals_stream * !!stream_active + * When both streams are active, disabling some bits for the current stream + * might break the other stream if these bits are used by it. * - * The following computes the overall differences between the setup for the - * to-disable stream and the active stream, a simple XOR: - * vals_disable ^ (vals_stream * !!(stream_active)) + * @vals : regvals of the current stream + * @avals: regvals of the opposite stream + * @aactive: active state of the opposite stream * - * The full expression adds a mask on all values we care about + * 1) XOR vals and avals to get the differences if the other stream is active; + * Otherwise, return current vals if the other stream is not active + * 2) AND the result of 1) with the current vals */ -#define fsl_ssi_disable_val(vals_disable, vals_stream, stream_active) \ - ((vals_disable) & \ - ((vals_disable) ^ ((vals_stream) * (u32)!!(stream_active)))) +#define _ssi_xor_shared_bits(vals, avals, aactive) \ + ((vals) ^ ((avals) * (aactive))) + +#define ssi_excl_shared_bits(vals, avals, aactive) \ + ((vals) & _ssi_xor_shared_bits(vals, avals, aactive))
/** * Enable or disable SSI configuration. @@ -445,16 +445,10 @@ static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable, bool dir = (&ssi->regvals[TX] == vals) ? TX : RX; struct regmap *regs = ssi->regs; struct fsl_ssi_regvals *avals; - int nr_active_streams; - int keep_active; - - nr_active_streams = !!(ssi->streams & BIT(TX)) + - !!(ssi->streams & BIT(RX)); + bool aactive;
- if (nr_active_streams - 1 > 0) - keep_active = 1; - else - keep_active = 0; + /* Check if the opposite stream is active */ + aactive = ssi->streams & BIT(!dir);
/* Get the opposite direction to keep its values untouched */ if (&ssi->regvals[RX] == vals) @@ -467,8 +461,7 @@ static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable, * To keep the other stream safe, exclude shared bits between * both streams, and get safe bits to disable current stream */ - u32 scr = fsl_ssi_disable_val(vals->scr, avals->scr, - keep_active); + u32 scr = ssi_excl_shared_bits(vals->scr, avals->scr, aactive); /* Safely disable SCR register for the stream */ regmap_update_bits(regs, REG_SSI_SCR, scr, 0);
@@ -483,7 +476,7 @@ static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable, * 2) Disable all remaining bits of both streams when last stream ends */ if (ssi->soc->offline_config) { - if ((enable && !nr_active_streams) || (!enable && !keep_active)) + if ((enable && !ssi->streams) || (!enable && !aactive)) fsl_ssi_rxtx_config(ssi, enable);
goto config_done; @@ -505,12 +498,9 @@ static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable, * To keep the other stream safe, exclude shared bits between * both streams, and get safe bits to disable current stream */ - sier = fsl_ssi_disable_val(vals->sier, avals->sier, - keep_active); - srcr = fsl_ssi_disable_val(vals->srcr, avals->srcr, - keep_active); - stcr = fsl_ssi_disable_val(vals->stcr, avals->stcr, - keep_active); + sier = ssi_excl_shared_bits(vals->sier, avals->sier, aactive); + srcr = ssi_excl_shared_bits(vals->srcr, avals->srcr, aactive); + stcr = ssi_excl_shared_bits(vals->stcr, avals->stcr, aactive);
/* Safely disable other control registers for the stream */ regmap_update_bits(regs, REG_SSI_SRCR, srcr, 0);
On 11.01.2018 07:43, Nicolin Chen wrote:
The define of fsl_ssi_disable_val is not so clear as it mixes two steps of calculations together. And those parameter names are also a bit long to read.
Since it just tries to exclude the shared bits from the regvals of current stream while the opposite stream is active, it's better to use something like ssi_excl_shared_bits.
This patch also bisects fsl_ssi_disable_val into two macros of two corresponding steps and then shortens its parameter names. It also updates callers in the fsl_ssi_config() accordingly.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org
sound/soc/fsl/fsl_ssi.c | 54 ++++++++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 32 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index aa14a5d..f026386 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -445,16 +445,10 @@ static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable, bool dir = (&ssi->regvals[TX] == vals) ? TX : RX; struct regmap *regs = ssi->regs; struct fsl_ssi_regvals *avals;
- int nr_active_streams;
- int keep_active;
- nr_active_streams = !!(ssi->streams & BIT(TX)) +
!!(ssi->streams & BIT(RX));
- bool aactive;
- if (nr_active_streams - 1 > 0)
keep_active = 1;
- else
keep_active = 0;
- /* Check if the opposite stream is active */
- aactive = ssi->streams & BIT(!dir);
^ Here an implicit assumption that either RX == 0, TX == 1 or RX == 1, TX == 0 still remains.
Maciej
On Sun, Jan 14, 2018 at 11:34:37PM +0100, Maciej S. Szmigiero wrote:
- /* Check if the opposite stream is active */
- aactive = ssi->streams & BIT(!dir);
^
Here an implicit assumption that either RX == 0, TX == 1 or RX == 1, TX == 0 still remains.
I would try to get rid of this !dir. However the regvals is defined as regvals[2] so this assumption (either RX == 0 or TX == 0) doesn't look wrong to me. I would prefer to add a comment to limit a potential modification to RX and TX.
The FIFO clear helper function is just one line of code now. So it could be cleaned up by removing it and calling regmap directly.
Meanwhile, FIFO clear could be applied to all use cases, not confined to AC97. So this patch also moves FIFO clear in the trigger() to fsl_ssi_config() and removes the AC97 check.
Note that SOR register is safe from offline_config HW limit.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org --- sound/soc/fsl/fsl_ssi.c | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index f026386..263c067 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -406,17 +406,6 @@ static void fsl_ssi_rxtx_config(struct fsl_ssi *ssi, bool enable) }
/** - * Clear remaining data in the FIFO to avoid dirty data or channel slipping - */ -static void fsl_ssi_fifo_clear(struct fsl_ssi *ssi, bool is_rx) -{ - bool tx = !is_rx; - - regmap_update_bits(ssi->regs, REG_SSI_SOR, - SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx)); -} - -/** * Exclude bits that are used by the opposite stream * * When both streams are active, disabling some bits for the current stream @@ -442,7 +431,8 @@ static void fsl_ssi_fifo_clear(struct fsl_ssi *ssi, bool is_rx) static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable, struct fsl_ssi_regvals *vals) { - bool dir = (&ssi->regvals[TX] == vals) ? TX : RX; + bool tx = &ssi->regvals[TX] == vals; + bool dir = tx ? TX : RX; struct regmap *regs = ssi->regs; struct fsl_ssi_regvals *avals; bool aactive; @@ -484,7 +474,9 @@ static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable,
/* Online configure single direction while SSI is running */ if (enable) { - fsl_ssi_fifo_clear(ssi, vals->scr & SSI_SCR_RE); + /* Clear FIFO to prevent dirty data or channel slipping */ + regmap_update_bits(ssi->regs, REG_SSI_SOR, + SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx));
regmap_update_bits(regs, REG_SSI_SRCR, vals->srcr, vals->srcr); regmap_update_bits(regs, REG_SSI_STCR, vals->stcr, vals->stcr); @@ -506,6 +498,10 @@ static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable, regmap_update_bits(regs, REG_SSI_SRCR, srcr, 0); regmap_update_bits(regs, REG_SSI_STCR, stcr, 0); regmap_update_bits(regs, REG_SSI_SIER, sier, 0); + + /* Clear FIFO to prevent dirty data or channel slipping */ + regmap_update_bits(ssi->regs, REG_SSI_SOR, + SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx)); }
config_done: @@ -1086,7 +1082,6 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai); - struct regmap *regs = ssi->regs;
switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -1111,14 +1106,6 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, return -EINVAL; }
- /* Clear corresponding FIFO */ - if (fsl_ssi_is_ac97(ssi)) { - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - regmap_write(regs, REG_SSI_SOR, SSI_SOR_TX_CLR); - else - regmap_write(regs, REG_SSI_SOR, SSI_SOR_RX_CLR); - } - return 0; }
The trigger() calls fsl_ssi_tx_config() and fsl_ssi_rx_config(), and both of them jump to fsl_ssi_config(). And fsl_ssi_config() later calls another fsl_ssi_rxtx_config().
However, the whole routine, especially fsl_ssi_config() function, is too complicated because of the folowing reasons: 1) It has to handle the concern of the opposite stream. 2) It has to handle cases of offline configurations support. 3) It has to handle enable and disable operations while they're mostly different.
Since the enable and disable routines have more differences than TX and RX rountines, this patch simplifies these helper functions with the following changes: - Changing to two helper functions of enable and disable instead of TX and RX. - Removing fsl_ssi_rxtx_config() by separately integrating it to two newly introduced enable & disable functions.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org --- sound/soc/fsl/fsl_ssi.c | 256 +++++++++++++++++++++++------------------------- 1 file changed, 122 insertions(+), 134 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 263c067..09a571a 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -378,31 +378,83 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) }
/** - * Enable or disable all rx/tx config flags at once + * Set SCR, SIER, STCR and SRCR registers with cached values in regvals + * + * Notes: + * 1) For offline_config SoCs, enable all necessary bits of both streams + * when 1st stream starts, even if the opposite stream will not start + * 2) It also clears FIFO before setting regvals; SOR is safe to set online */ -static void fsl_ssi_rxtx_config(struct fsl_ssi *ssi, bool enable) +static void fsl_ssi_config_enable(struct fsl_ssi *ssi, bool tx) { - struct regmap *regs = ssi->regs; struct fsl_ssi_regvals *vals = ssi->regvals; + bool dir = tx ? TX : RX; + u32 sier, srcr, stcr;
- if (enable) { - regmap_update_bits(regs, REG_SSI_SIER, - vals[RX].sier | vals[TX].sier, - vals[RX].sier | vals[TX].sier); - regmap_update_bits(regs, REG_SSI_SRCR, - vals[RX].srcr | vals[TX].srcr, - vals[RX].srcr | vals[TX].srcr); - regmap_update_bits(regs, REG_SSI_STCR, - vals[RX].stcr | vals[TX].stcr, - vals[RX].stcr | vals[TX].stcr); + /* Clear dirty data in the FIFO; It also prevents channel slipping */ + regmap_update_bits(ssi->regs, REG_SSI_SOR, + SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx)); + + /* + * On offline_config SoCs, SxCR and SIER are already configured when + * the previous stream started. So skip all SxCR and SIER settings + * to prevent online reconfigurations, then jump to set SCR directly + */ + if (ssi->soc->offline_config && ssi->streams) + goto enable_scr; + + if (ssi->soc->offline_config) { + /* + * Online reconfiguration not supported, so enable all bits for + * both streams at once to avoid necessity of reconfigurations + */ + srcr = vals[RX].srcr | vals[TX].srcr; + stcr = vals[RX].stcr | vals[TX].stcr; + sier = vals[RX].sier | vals[TX].sier; } else { - regmap_update_bits(regs, REG_SSI_SRCR, - vals[RX].srcr | vals[TX].srcr, 0); - regmap_update_bits(regs, REG_SSI_STCR, - vals[RX].stcr | vals[TX].stcr, 0); - regmap_update_bits(regs, REG_SSI_SIER, - vals[RX].sier | vals[TX].sier, 0); + /* Otherwise, only set bits for the current stream */ + srcr = vals[dir].srcr; + stcr = vals[dir].stcr; + sier = vals[dir].sier; } + + /* Configure SRCR, STCR and SIER at once */ + regmap_update_bits(ssi->regs, REG_SSI_SRCR, srcr, srcr); + regmap_update_bits(ssi->regs, REG_SSI_STCR, stcr, stcr); + regmap_update_bits(ssi->regs, REG_SSI_SIER, sier, sier); + +enable_scr: + /* + * Start DMA before setting TE to avoid FIFO underrun + * which may cause a channel slip or a channel swap + * + * TODO: FIQ cases might also need this upon testing + */ + if (ssi->use_dma && tx) { + int try = 100; + u32 sfcsr; + + /* Enable SSI first to send TX DMA request */ + regmap_update_bits(ssi->regs, REG_SSI_SCR, + SSI_SCR_SSIEN, SSI_SCR_SSIEN); + + /* Busy wait until TX FIFO not empty -- DMA working */ + do { + regmap_read(ssi->regs, REG_SSI_SFCSR, &sfcsr); + if (SSI_SFCSR_TFCNT0(sfcsr)) + break; + } while (--try); + + /* FIFO still empty -- something might be wrong */ + if (!SSI_SFCSR_TFCNT0(sfcsr)) + dev_warn(ssi->dev, "Timeout waiting TX FIFO filling\n"); + } + /* Enable all remaining bits in SCR */ + regmap_update_bits(ssi->regs, REG_SSI_SCR, + vals[dir].scr, vals[dir].scr); + + /* Log the enabled stream to the mask */ + ssi->streams |= BIT(dir); }
/** @@ -426,66 +478,53 @@ static void fsl_ssi_rxtx_config(struct fsl_ssi *ssi, bool enable) ((vals) & _ssi_xor_shared_bits(vals, avals, aactive))
/** - * Enable or disable SSI configuration. + * Unset SCR, SIER, STCR and SRCR registers with cached values in regvals + * + * Notes: + * 1) For offline_config SoCs, to avoid online reconfigurations, disable all + * bits of both streams at once when the last stream is abort to end + * 2) It also clears FIFO after unsetting regvals; SOR is safe to set online */ -static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable, - struct fsl_ssi_regvals *vals) +static void fsl_ssi_config_disable(struct fsl_ssi *ssi, bool tx) { - bool tx = &ssi->regvals[TX] == vals; + struct fsl_ssi_regvals *vals, *avals; + u32 sier, srcr, stcr, scr; bool dir = tx ? TX : RX; - struct regmap *regs = ssi->regs; - struct fsl_ssi_regvals *avals; bool aactive;
/* Check if the opposite stream is active */ aactive = ssi->streams & BIT(!dir);
- /* Get the opposite direction to keep its values untouched */ - if (&ssi->regvals[RX] == vals) - avals = &ssi->regvals[TX]; - else - avals = &ssi->regvals[RX]; + vals = &ssi->regvals[dir];
- if (!enable) { - /* - * To keep the other stream safe, exclude shared bits between - * both streams, and get safe bits to disable current stream - */ - u32 scr = ssi_excl_shared_bits(vals->scr, avals->scr, aactive); - /* Safely disable SCR register for the stream */ - regmap_update_bits(regs, REG_SSI_SCR, scr, 0); - - /* Log the disabled stream to the mask */ - ssi->streams &= ~BIT(dir); - } + /* Get regvals of the opposite stream to keep opposite stream safe */ + avals = &ssi->regvals[!dir];
/* - * For cases where online configuration is not supported, - * 1) Enable all necessary bits of both streams when 1st stream starts - * even if the opposite stream will not start - * 2) Disable all remaining bits of both streams when last stream ends + * To keep the other stream safe, exclude shared bits between + * both streams, and get safe bits to disable current stream */ - if (ssi->soc->offline_config) { - if ((enable && !ssi->streams) || (!enable && !aactive)) - fsl_ssi_rxtx_config(ssi, enable); + scr = ssi_excl_shared_bits(vals->scr, avals->scr, aactive);
- goto config_done; - } + /* Disable safe bits of SCR register for the current stream */ + regmap_update_bits(ssi->regs, REG_SSI_SCR, scr, 0);
- /* Online configure single direction while SSI is running */ - if (enable) { - /* Clear FIFO to prevent dirty data or channel slipping */ - regmap_update_bits(ssi->regs, REG_SSI_SOR, - SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx)); + /* Log the disabled stream to the mask */ + ssi->streams &= ~BIT(dir);
- regmap_update_bits(regs, REG_SSI_SRCR, vals->srcr, vals->srcr); - regmap_update_bits(regs, REG_SSI_STCR, vals->stcr, vals->stcr); - regmap_update_bits(regs, REG_SSI_SIER, vals->sier, vals->sier); - } else { - u32 sier; - u32 srcr; - u32 stcr; + /* + * On offline_config SoCs, if the other stream is active, skip + * SxCR and SIER settings to prevent online reconfigurations + */ + if (ssi->soc->offline_config && aactive) + goto fifo_clear;
+ if (ssi->soc->offline_config) { + /* Now there is only current stream active, disable all bits */ + srcr = vals->srcr | avals->srcr; + stcr = vals->stcr | avals->stcr; + sier = vals->sier | avals->sier; + } else { /* * To keep the other stream safe, exclude shared bits between * both streams, and get safe bits to disable current stream @@ -493,57 +532,17 @@ static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable, sier = ssi_excl_shared_bits(vals->sier, avals->sier, aactive); srcr = ssi_excl_shared_bits(vals->srcr, avals->srcr, aactive); stcr = ssi_excl_shared_bits(vals->stcr, avals->stcr, aactive); - - /* Safely disable other control registers for the stream */ - regmap_update_bits(regs, REG_SSI_SRCR, srcr, 0); - regmap_update_bits(regs, REG_SSI_STCR, stcr, 0); - regmap_update_bits(regs, REG_SSI_SIER, sier, 0); - - /* Clear FIFO to prevent dirty data or channel slipping */ - regmap_update_bits(ssi->regs, REG_SSI_SOR, - SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx)); }
-config_done: - /* Enabling of subunits is done after configuration */ - if (enable) { - /* - * Start DMA before setting TE to avoid FIFO underrun - * which may cause a channel slip or a channel swap - * - * TODO: FIQ cases might also need this upon testing - */ - if (ssi->use_dma && (vals->scr & SSI_SCR_TE)) { - int i; - int max_loop = 100; - - /* Enable SSI first to send TX DMA request */ - regmap_update_bits(regs, REG_SSI_SCR, - SSI_SCR_SSIEN, SSI_SCR_SSIEN); - - /* Busy wait until TX FIFO not empty -- DMA working */ - for (i = 0; i < max_loop; i++) { - u32 sfcsr; - regmap_read(regs, REG_SSI_SFCSR, &sfcsr); - if (SSI_SFCSR_TFCNT0(sfcsr)) - break; - } - if (i == max_loop) { - dev_err(ssi->dev, - "Timeout waiting TX FIFO filling\n"); - } - } - /* Enable all remaining bits */ - regmap_update_bits(regs, REG_SSI_SCR, vals->scr, vals->scr); - - /* Log the enabled stream to the mask */ - ssi->streams |= BIT(dir); - } -} + /* Clear configurations of SRCR, STCR and SIER at once */ + regmap_update_bits(ssi->regs, REG_SSI_SRCR, srcr, 0); + regmap_update_bits(ssi->regs, REG_SSI_STCR, stcr, 0); + regmap_update_bits(ssi->regs, REG_SSI_SIER, sier, 0);
-static void fsl_ssi_rx_config(struct fsl_ssi *ssi, bool enable) -{ - fsl_ssi_config(ssi, enable, &ssi->regvals[RX]); +fifo_clear: + /* Clear remaining data in the FIFO */ + regmap_update_bits(ssi->regs, REG_SSI_SOR, + SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx)); }
static void fsl_ssi_tx_ac97_saccst_setup(struct fsl_ssi *ssi) @@ -559,21 +558,6 @@ static void fsl_ssi_tx_ac97_saccst_setup(struct fsl_ssi *ssi) } }
-static void fsl_ssi_tx_config(struct fsl_ssi *ssi, bool enable) -{ - /* - * SACCST might be modified via AC Link by a CODEC if it sends - * extra bits in their SLOTREQ requests, which'll accidentally - * send valid data to slots other than normal playback slots. - * - * To be safe, configure SACCST right before TX starts. - */ - if (enable && fsl_ssi_is_ac97(ssi)) - fsl_ssi_tx_ac97_saccst_setup(ssi); - - fsl_ssi_config(ssi, enable, &ssi->regvals[TX]); -} - /** * Cache critical bits of SIER, SRCR, STCR and SCR to later set them safely */ @@ -1082,24 +1066,28 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - fsl_ssi_tx_config(ssi, true); - else - fsl_ssi_rx_config(ssi, true); + /* + * SACCST might be modified via AC Link by a CODEC if it sends + * extra bits in their SLOTREQ requests, which'll accidentally + * send valid data to slots other than normal playback slots. + * + * To be safe, configure SACCST right before TX starts. + */ + if (tx && fsl_ssi_is_ac97(ssi)) + fsl_ssi_tx_ac97_saccst_setup(ssi); + fsl_ssi_config_enable(ssi, tx); break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - fsl_ssi_tx_config(ssi, false); - else - fsl_ssi_rx_config(ssi, false); + fsl_ssi_config_disable(ssi, tx); break;
default:
On 11.01.2018 07:43, Nicolin Chen wrote:
The trigger() calls fsl_ssi_tx_config() and fsl_ssi_rx_config(), and both of them jump to fsl_ssi_config(). And fsl_ssi_config() later calls another fsl_ssi_rxtx_config().
However, the whole routine, especially fsl_ssi_config() function, is too complicated because of the folowing reasons:
- It has to handle the concern of the opposite stream.
- It has to handle cases of offline configurations support.
- It has to handle enable and disable operations while they're mostly different.
Since the enable and disable routines have more differences than TX and RX rountines, this patch simplifies these helper functions with the following changes:
- Changing to two helper functions of enable and disable instead of TX and RX.
- Removing fsl_ssi_rxtx_config() by separately integrating it to two newly introduced enable & disable functions.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org
sound/soc/fsl/fsl_ssi.c | 256 +++++++++++++++++++++++------------------------- 1 file changed, 122 insertions(+), 134 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 263c067..09a571a 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -378,31 +378,83 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) }
/**
- Enable or disable all rx/tx config flags at once
- Set SCR, SIER, STCR and SRCR registers with cached values in regvals
- Notes:
- For offline_config SoCs, enable all necessary bits of both streams
- when 1st stream starts, even if the opposite stream will not start
*/
- It also clears FIFO before setting regvals; SOR is safe to set online
-static void fsl_ssi_rxtx_config(struct fsl_ssi *ssi, bool enable) +static void fsl_ssi_config_enable(struct fsl_ssi *ssi, bool tx) {
- struct regmap *regs = ssi->regs; struct fsl_ssi_regvals *vals = ssi->regvals;
- bool dir = tx ? TX : RX;
A similar case as in patch 3 of a bool variable used for a bit index.
- u32 sier, srcr, stcr;
- if (enable) {
regmap_update_bits(regs, REG_SSI_SIER,
vals[RX].sier | vals[TX].sier,
vals[RX].sier | vals[TX].sier);
regmap_update_bits(regs, REG_SSI_SRCR,
vals[RX].srcr | vals[TX].srcr,
vals[RX].srcr | vals[TX].srcr);
regmap_update_bits(regs, REG_SSI_STCR,
vals[RX].stcr | vals[TX].stcr,
vals[RX].stcr | vals[TX].stcr);
- /* Clear dirty data in the FIFO; It also prevents channel slipping */
- regmap_update_bits(ssi->regs, REG_SSI_SOR,
SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx));
- /*
* On offline_config SoCs, SxCR and SIER are already configured when
* the previous stream started. So skip all SxCR and SIER settings
* to prevent online reconfigurations, then jump to set SCR directly
*/
- if (ssi->soc->offline_config && ssi->streams)
goto enable_scr;
- if (ssi->soc->offline_config) {
/*
* Online reconfiguration not supported, so enable all bits for
* both streams at once to avoid necessity of reconfigurations
*/
srcr = vals[RX].srcr | vals[TX].srcr;
stcr = vals[RX].stcr | vals[TX].stcr;
} else {sier = vals[RX].sier | vals[TX].sier;
regmap_update_bits(regs, REG_SSI_SRCR,
vals[RX].srcr | vals[TX].srcr, 0);
regmap_update_bits(regs, REG_SSI_STCR,
vals[RX].stcr | vals[TX].stcr, 0);
regmap_update_bits(regs, REG_SSI_SIER,
vals[RX].sier | vals[TX].sier, 0);
/* Otherwise, only set bits for the current stream */
srcr = vals[dir].srcr;
stcr = vals[dir].stcr;
}sier = vals[dir].sier;
- /* Configure SRCR, STCR and SIER at once */
- regmap_update_bits(ssi->regs, REG_SSI_SRCR, srcr, srcr);
- regmap_update_bits(ssi->regs, REG_SSI_STCR, stcr, stcr);
- regmap_update_bits(ssi->regs, REG_SSI_SIER, sier, sier);
+enable_scr:
- /*
* Start DMA before setting TE to avoid FIFO underrun
* which may cause a channel slip or a channel swap
*
* TODO: FIQ cases might also need this upon testing
*/
- if (ssi->use_dma && tx) {
int try = 100;
u32 sfcsr;
/* Enable SSI first to send TX DMA request */
regmap_update_bits(ssi->regs, REG_SSI_SCR,
SSI_SCR_SSIEN, SSI_SCR_SSIEN);
/* Busy wait until TX FIFO not empty -- DMA working */
do {
regmap_read(ssi->regs, REG_SSI_SFCSR, &sfcsr);
if (SSI_SFCSR_TFCNT0(sfcsr))
break;
} while (--try);
/* FIFO still empty -- something might be wrong */
if (!SSI_SFCSR_TFCNT0(sfcsr))
dev_warn(ssi->dev, "Timeout waiting TX FIFO filling\n");
- }
- /* Enable all remaining bits in SCR */
- regmap_update_bits(ssi->regs, REG_SSI_SCR,
vals[dir].scr, vals[dir].scr);
- /* Log the enabled stream to the mask */
- ssi->streams |= BIT(dir);
}
/** @@ -426,66 +478,53 @@ static void fsl_ssi_rxtx_config(struct fsl_ssi *ssi, bool enable) ((vals) & _ssi_xor_shared_bits(vals, avals, aactive))
/**
- Enable or disable SSI configuration.
- Unset SCR, SIER, STCR and SRCR registers with cached values in regvals
- Notes:
- For offline_config SoCs, to avoid online reconfigurations, disable all
- bits of both streams at once when the last stream is abort to end
*/
- It also clears FIFO after unsetting regvals; SOR is safe to set online
-static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable,
struct fsl_ssi_regvals *vals)
+static void fsl_ssi_config_disable(struct fsl_ssi *ssi, bool tx) {
- bool tx = &ssi->regvals[TX] == vals;
- struct fsl_ssi_regvals *vals, *avals;
- u32 sier, srcr, stcr, scr; bool dir = tx ? TX : RX;
struct regmap *regs = ssi->regs;
struct fsl_ssi_regvals *avals; bool aactive;
/* Check if the opposite stream is active */ aactive = ssi->streams & BIT(!dir);
/* Get the opposite direction to keep its values untouched */
if (&ssi->regvals[RX] == vals)
avals = &ssi->regvals[TX];
else
avals = &ssi->regvals[RX];
- vals = &ssi->regvals[dir];>
- if (!enable) {
/*
* To keep the other stream safe, exclude shared bits between
* both streams, and get safe bits to disable current stream
*/
u32 scr = ssi_excl_shared_bits(vals->scr, avals->scr, aactive);
/* Safely disable SCR register for the stream */
regmap_update_bits(regs, REG_SSI_SCR, scr, 0);
/* Log the disabled stream to the mask */
ssi->streams &= ~BIT(dir);
- }
- /* Get regvals of the opposite stream to keep opposite stream safe */
- avals = &ssi->regvals[!dir];
^ The same implicit assumption here as in patch 4.
Maciej
The _fsl_ssi_set_dai_fmt() bypasses an undefined format for AC97 mode. However, it's not really necessary if AC97 has its complete format defined.
So this patch adds a DAIFMT macro of complete format including a clock direction and polarity.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org --- sound/soc/fsl/fsl_ssi.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 09a571a..b449b8b 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -86,6 +86,16 @@ SNDRV_PCM_FMTBIT_S24_LE) #endif
+/* + * In AC97 mode, TXDIR bit is forced to 0 and TFDIR bit is forced to 1: + * - SSI inputs external bit clock and outputs frame sync clock -- CBM_CFS + * - Also have NB_NF to mark these two clocks will not be inverted + */ +#define FSLSSI_AC97_DAIFMT \ + (SND_SOC_DAIFMT_AC97 | \ + SND_SOC_DAIFMT_CBM_CFS | \ + SND_SOC_DAIFMT_NB_NF) + #define FSLSSI_SIER_DBG_RX_FLAGS \ (SSI_SIER_RFF0_EN | \ SSI_SIER_RLS_EN | \ @@ -959,8 +969,7 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, scr &= ~SSI_SCR_SYS_CLK_EN; break; default: - if (!fsl_ssi_is_ac97(ssi)) - return -EINVAL; + return -EINVAL; }
stcr |= strcr; @@ -1367,7 +1376,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) sprop = of_get_property(np, "fsl,mode", NULL); if (sprop) { if (!strcmp(sprop, "ac97-slave")) - ssi->dai_fmt = SND_SOC_DAIFMT_AC97; + ssi->dai_fmt = FSLSSI_AC97_DAIFMT; }
/* Select DMA or FIQ */
This patch cleans fsl_ssi_setup_regvals() by following changes: 1) Moving DBG bits to the first lines. 2) Setting SSIE, RE/TE as default and cleaning it for AC97
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org --- sound/soc/fsl/fsl_ssi.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index b449b8b..4399a22 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -575,18 +575,16 @@ static void fsl_ssi_setup_regvals(struct fsl_ssi *ssi) { struct fsl_ssi_regvals *vals = ssi->regvals;
- vals[RX].sier = SSI_SIER_RFF0_EN; + vals[RX].sier = SSI_SIER_RFF0_EN | FSLSSI_SIER_DBG_RX_FLAGS; vals[RX].srcr = SSI_SRCR_RFEN0; - vals[RX].scr = 0; - vals[TX].sier = SSI_SIER_TFE0_EN; + vals[RX].scr = SSI_SCR_SSIEN | SSI_SCR_RE; + vals[TX].sier = SSI_SIER_TFE0_EN | FSLSSI_SIER_DBG_TX_FLAGS; vals[TX].stcr = SSI_STCR_TFEN0; - vals[TX].scr = 0; + vals[TX].scr = SSI_SCR_SSIEN | SSI_SCR_TE;
/* AC97 has already enabled SSIEN, RE and TE, so ignore them */ - if (!fsl_ssi_is_ac97(ssi)) { - vals[RX].scr = SSI_SCR_SSIEN | SSI_SCR_RE; - vals[TX].scr = SSI_SCR_SSIEN | SSI_SCR_TE; - } + if (fsl_ssi_is_ac97(ssi)) + vals[RX].scr = vals[TX].scr = 0;
if (ssi->use_dma) { vals[RX].sier |= SSI_SIER_RDMAE; @@ -595,9 +593,6 @@ static void fsl_ssi_setup_regvals(struct fsl_ssi *ssi) vals[RX].sier |= SSI_SIER_RIE; vals[TX].sier |= SSI_SIER_TIE; } - - vals[RX].sier |= FSLSSI_SIER_DBG_RX_FLAGS; - vals[TX].sier |= FSLSSI_SIER_DBG_TX_FLAGS; }
static void fsl_ssi_setup_ac97(struct fsl_ssi *ssi)
It'd be safer to enable both FIFOs for TX or RX at the same time.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org --- sound/soc/fsl/fsl_ssi.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 4399a22..e75b57d 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -586,6 +586,11 @@ static void fsl_ssi_setup_regvals(struct fsl_ssi *ssi) if (fsl_ssi_is_ac97(ssi)) vals[RX].scr = vals[TX].scr = 0;
+ if (ssi->use_dual_fifo) { + vals[RX].srcr |= SSI_SRCR_RFEN1; + vals[TX].stcr |= SSI_STCR_TFEN1; + } + if (ssi->use_dma) { vals[RX].sier |= SSI_SIER_RDMAE; vals[TX].sier |= SSI_SIER_TDMAE; @@ -986,14 +991,9 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, SSI_SFCSR_TFWM0(wm) | SSI_SFCSR_RFWM0(wm) | SSI_SFCSR_TFWM1(wm) | SSI_SFCSR_RFWM1(wm));
- if (ssi->use_dual_fifo) { - regmap_update_bits(regs, REG_SSI_SRCR, - SSI_SRCR_RFEN1, SSI_SRCR_RFEN1); - regmap_update_bits(regs, REG_SSI_STCR, - SSI_STCR_TFEN1, SSI_STCR_TFEN1); + if (ssi->use_dual_fifo) regmap_update_bits(regs, REG_SSI_SCR, SSI_SCR_TCH_EN, SSI_SCR_TCH_EN); - }
if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_AC97) fsl_ssi_setup_ac97(ssi);
Since there is a helper function, use it to help readability.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org --- sound/soc/fsl/fsl_ssi.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index e75b57d..2c179a7 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1105,10 +1105,9 @@ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai) { struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai);
- if (ssi->soc->imx && ssi->use_dma) { - dai->playback_dma_data = &ssi->dma_params_tx; - dai->capture_dma_data = &ssi->dma_params_rx; - } + if (ssi->soc->imx && ssi->use_dma) + snd_soc_dai_init_dma_data(dai, &ssi->dma_params_tx, + &ssi->dma_params_rx);
return 0; }
The probe() could handle some one-time configurations since they will not be changed once being configured.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org --- sound/soc/fsl/fsl_ssi.c | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 2c179a7..36e370e 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -860,7 +860,6 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, { struct regmap *regs = ssi->regs; u32 strcr = 0, stcr, srcr, scr, mask; - u8 wm;
ssi->dai_fmt = fmt;
@@ -869,8 +868,6 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, return -EINVAL; }
- fsl_ssi_setup_regvals(ssi); - regmap_read(regs, REG_SSI_SCR, &scr); scr &= ~(SSI_SCR_SYN | SSI_SCR_I2S_MODE_MASK); /* Synchronize frame sync clock for TE to avoid data slipping */ @@ -985,16 +982,6 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, regmap_write(regs, REG_SSI_SRCR, srcr); regmap_write(regs, REG_SSI_SCR, scr);
- wm = ssi->fifo_watermark; - - regmap_write(regs, REG_SSI_SFCSR, - SSI_SFCSR_TFWM0(wm) | SSI_SFCSR_RFWM0(wm) | - SSI_SFCSR_TFWM1(wm) | SSI_SFCSR_RFWM1(wm)); - - if (ssi->use_dual_fifo) - regmap_update_bits(regs, REG_SSI_SCR, - SSI_SCR_TCH_EN, SSI_SCR_TCH_EN); - if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_AC97) fsl_ssi_setup_ac97(ssi);
@@ -1244,6 +1231,29 @@ static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = { };
/** + * Initialize SSI registers + */ +static int fsl_ssi_hw_init(struct fsl_ssi *ssi) +{ + u32 wm = ssi->fifo_watermark; + + /* Initialize regvals */ + fsl_ssi_setup_regvals(ssi); + + /* Set watermarks */ + regmap_write(ssi->regs, REG_SSI_SFCSR, + SSI_SFCSR_TFWM0(wm) | SSI_SFCSR_RFWM0(wm) | + SSI_SFCSR_TFWM1(wm) | SSI_SFCSR_RFWM1(wm)); + + /* Enable Dual FIFO mode */ + if (ssi->use_dual_fifo) + regmap_update_bits(ssi->regs, REG_SSI_SCR, + SSI_SCR_TCH_EN, SSI_SCR_TCH_EN); + + return 0; +} + +/** * Make every character in a string lower-case */ static void make_lowercase(char *s) @@ -1528,6 +1538,9 @@ static int fsl_ssi_probe(struct platform_device *pdev) if (ssi->dai_fmt) _fsl_ssi_set_dai_fmt(dev, ssi, ssi->dai_fmt);
+ /* Initially configures SSI registers */ + fsl_ssi_hw_init(ssi); + if (fsl_ssi_is_ac97(ssi)) { u32 ssi_idx;
AC97 configures most of registers earlier to start a communication with CODECs in order to successfully initialize CODEC. Currently, _fsl_ssi_set_dai_fmt() and fsl_ssi_setup_ac97() are called to get all SSI registers properly set.
Since now the driver has a fsl_ssi_hw_init() to handle all register initial settings, this patch moves those register settings of AC97 to the fsl_ssi_hw_init() as well.
Meanwhile it applies _fsl_ssi_set_dai_fmt() call to AC97 only since other formats would be configured via normal set_dai_fmt() directly.
This patch also adds fsl_ssi_hw_clean() to cleanup control bits for AC97 in the platform remote() function.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org --- sound/soc/fsl/fsl_ssi.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 36e370e..178c192 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -982,9 +982,6 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, regmap_write(regs, REG_SSI_SRCR, srcr); regmap_write(regs, REG_SSI_SCR, scr);
- if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_AC97) - fsl_ssi_setup_ac97(ssi); - return 0; }
@@ -1250,10 +1247,28 @@ static int fsl_ssi_hw_init(struct fsl_ssi *ssi) regmap_update_bits(ssi->regs, REG_SSI_SCR, SSI_SCR_TCH_EN, SSI_SCR_TCH_EN);
+ /* AC97 should start earlier to communicate with CODECs */ + if (fsl_ssi_is_ac97(ssi)) { + _fsl_ssi_set_dai_fmt(ssi->dev, ssi, ssi->dai_fmt); + fsl_ssi_setup_ac97(ssi); + } + return 0; }
/** + * Clear SSI registers + */ +static void fsl_ssi_hw_clean(struct fsl_ssi *ssi) +{ + /* Disable registers for AC97 */ + if (fsl_ssi_is_ac97(ssi)) { + regmap_write(ssi->regs, REG_SSI_SCR, 0); + regmap_write(ssi->regs, REG_SSI_SACNT, 0); + regmap_write(ssi->regs, REG_SSI_SOR, 0); + } +} +/** * Make every character in a string lower-case */ static void make_lowercase(char *s) @@ -1535,9 +1550,6 @@ static int fsl_ssi_probe(struct platform_device *pdev) }
done: - if (ssi->dai_fmt) - _fsl_ssi_set_dai_fmt(dev, ssi, ssi->dai_fmt); - /* Initially configures SSI registers */ fsl_ssi_hw_init(ssi);
@@ -1582,6 +1594,8 @@ static int fsl_ssi_remove(struct platform_device *pdev) { struct fsl_ssi *ssi = dev_get_drvdata(&pdev->dev);
+ fsl_ssi_hw_clean(ssi); + fsl_ssi_debugfs_remove(&ssi->dbg_stats);
if (ssi->pdev)
The _fsl_ssi_set_dai_fmt() is a helper function being called from fsl_ssi_set_dai_fmt() as an ASoC operation and fsl_ssi_hw_init() mainly for AC97 format initialization.
This patch cleans the _fsl_ssi_set_dai_fmt() in following ways: * Removing *dev pointer in the parameters as it's included in the *ssi pointer of struct fsl_ssi. * Using regmap_update_bits() instead of regmap_read() with masking the value manually. * Removing TXBIT0 configurations since this bit is set to 1 as its reset value and there is no use case so far to unset it. And it is safe to remove since regmap_update_bits() won't touch it. * Moving baudclk check to the switch-case routine to skip the I2S master check. And moving SxCCR.DC settings after baudclk check. * Adding format settings for SND_SOC_DAIFMT_AC97 like others.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org --- sound/soc/fsl/fsl_ssi.c | 70 ++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 39 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 178c192..213962a 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -855,42 +855,27 @@ static int fsl_ssi_hw_free(struct snd_pcm_substream *substream, return 0; }
-static int _fsl_ssi_set_dai_fmt(struct device *dev, - struct fsl_ssi *ssi, unsigned int fmt) +static int _fsl_ssi_set_dai_fmt(struct fsl_ssi *ssi, unsigned int fmt) { - struct regmap *regs = ssi->regs; - u32 strcr = 0, stcr, srcr, scr, mask; + u32 strcr = 0, scr = 0, stcr, srcr, mask;
ssi->dai_fmt = fmt;
- if (fsl_ssi_is_i2s_master(ssi) && IS_ERR(ssi->baudclk)) { - dev_err(dev, "missing baudclk for master mode\n"); - return -EINVAL; - } - - regmap_read(regs, REG_SSI_SCR, &scr); - scr &= ~(SSI_SCR_SYN | SSI_SCR_I2S_MODE_MASK); /* Synchronize frame sync clock for TE to avoid data slipping */ scr |= SSI_SCR_SYNC_TX_FS;
- mask = SSI_STCR_TXBIT0 | SSI_STCR_TFDIR | SSI_STCR_TXDIR | - SSI_STCR_TSCKP | SSI_STCR_TFSI | SSI_STCR_TFSL | SSI_STCR_TEFS; - regmap_read(regs, REG_SSI_STCR, &stcr); - regmap_read(regs, REG_SSI_SRCR, &srcr); - stcr &= ~mask; - srcr &= ~mask; - /* Use Network mode as default */ ssi->i2s_net = SSI_SCR_NET; switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - regmap_update_bits(regs, REG_SSI_STCCR, - SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(2)); - regmap_update_bits(regs, REG_SSI_SRCCR, - SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(2)); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFS: case SND_SOC_DAIFMT_CBS_CFS: + if (IS_ERR(ssi->baudclk)) { + dev_err(ssi->dev, + "missing baudclk for master mode\n"); + return -EINVAL; + } ssi->i2s_net |= SSI_SCR_I2S_MODE_MASTER; break; case SND_SOC_DAIFMT_CBM_CFM: @@ -900,30 +885,34 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, return -EINVAL; }
+ regmap_update_bits(ssi->regs, REG_SSI_STCCR, + SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(2)); + regmap_update_bits(ssi->regs, REG_SSI_SRCCR, + SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(2)); + /* Data on rising edge of bclk, frame low, 1clk before data */ - strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP | - SSI_STCR_TXBIT0 | SSI_STCR_TEFS; + strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP | SSI_STCR_TEFS; break; case SND_SOC_DAIFMT_LEFT_J: /* Data on rising edge of bclk, frame high */ - strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP; + strcr |= SSI_STCR_TSCKP; break; case SND_SOC_DAIFMT_DSP_A: /* Data on rising edge of bclk, frame high, 1clk before data */ - strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP | - SSI_STCR_TXBIT0 | SSI_STCR_TEFS; + strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP | SSI_STCR_TEFS; break; case SND_SOC_DAIFMT_DSP_B: /* Data on rising edge of bclk, frame high */ - strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP | SSI_STCR_TXBIT0; + strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP; break; case SND_SOC_DAIFMT_AC97: /* Data on falling edge of bclk, frame high, 1clk before data */ - ssi->i2s_net |= SSI_SCR_I2S_MODE_NORMAL; + strcr |= SSI_STCR_TEFS; break; default: return -EINVAL; } + scr |= ssi->i2s_net;
/* DAI clock inversion */ @@ -957,20 +946,17 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, break; case SND_SOC_DAIFMT_CBM_CFM: /* Input bit or frame sync clocks */ - scr &= ~SSI_SCR_SYS_CLK_EN; break; case SND_SOC_DAIFMT_CBM_CFS: /* Input bit clock but output frame sync clock */ - strcr &= ~SSI_STCR_TXDIR; strcr |= SSI_STCR_TFDIR; - scr &= ~SSI_SCR_SYS_CLK_EN; break; default: return -EINVAL; }
- stcr |= strcr; - srcr |= strcr; + stcr = strcr; + srcr = strcr;
/* Set SYN mode and clear RXDIR bit when using SYN or AC97 mode */ if (ssi->cpu_dai_drv.symmetric_rates || fsl_ssi_is_ac97(ssi)) { @@ -978,9 +964,15 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev, scr |= SSI_SCR_SYN; }
- regmap_write(regs, REG_SSI_STCR, stcr); - regmap_write(regs, REG_SSI_SRCR, srcr); - regmap_write(regs, REG_SSI_SCR, scr); + mask = SSI_STCR_TFDIR | SSI_STCR_TXDIR | SSI_STCR_TSCKP | + SSI_STCR_TFSI | SSI_STCR_TFSL | SSI_STCR_TEFS; + + regmap_update_bits(ssi->regs, REG_SSI_STCR, mask, stcr); + regmap_update_bits(ssi->regs, REG_SSI_SRCR, mask, srcr); + + mask = SSI_SCR_SYNC_TX_FS | SSI_SCR_I2S_MODE_MASK | + SSI_SCR_SYS_CLK_EN | SSI_SCR_SYN; + regmap_update_bits(ssi->regs, REG_SSI_SCR, mask, scr);
return 0; } @@ -996,7 +988,7 @@ static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) if (fsl_ssi_is_ac97(ssi)) return 0;
- return _fsl_ssi_set_dai_fmt(dai->dev, ssi, fmt); + return _fsl_ssi_set_dai_fmt(ssi, fmt); }
/** @@ -1249,7 +1241,7 @@ static int fsl_ssi_hw_init(struct fsl_ssi *ssi)
/* AC97 should start earlier to communicate with CODECs */ if (fsl_ssi_is_ac97(ssi)) { - _fsl_ssi_set_dai_fmt(ssi->dev, ssi, ssi->dai_fmt); + _fsl_ssi_set_dai_fmt(ssi, ssi->dai_fmt); fsl_ssi_setup_ac97(ssi); }
On 11.01.2018 07:43, Nicolin Chen wrote:
The _fsl_ssi_set_dai_fmt() is a helper function being called from fsl_ssi_set_dai_fmt() as an ASoC operation and fsl_ssi_hw_init() mainly for AC97 format initialization.
This patch cleans the _fsl_ssi_set_dai_fmt() in following ways:
- Removing *dev pointer in the parameters as it's included in the *ssi pointer of struct fsl_ssi.
- Using regmap_update_bits() instead of regmap_read() with masking the value manually.
- Removing TXBIT0 configurations since this bit is set to 1 as its reset value and there is no use case so far to unset it. And it is safe to remove since regmap_update_bits() won't touch it.
The old code set this bit in any mode other than AC'97 (where the hardware always treats this bit as set regardless of the actual value). I would play safe here and not rely on this bit being set by a SSI reset on all SSI models.
- Moving baudclk check to the switch-case routine to skip the I2S master check. And moving SxCCR.DC settings after baudclk check.
- Adding format settings for SND_SOC_DAIFMT_AC97 like others.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org
sound/soc/fsl/fsl_ssi.c | 70 ++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 39 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 178c192..213962a 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -855,42 +855,27 @@ static int fsl_ssi_hw_free(struct snd_pcm_substream *substream, return 0; }
-static int _fsl_ssi_set_dai_fmt(struct device *dev,
struct fsl_ssi *ssi, unsigned int fmt)
+static int _fsl_ssi_set_dai_fmt(struct fsl_ssi *ssi, unsigned int fmt) {
- struct regmap *regs = ssi->regs;
- u32 strcr = 0, stcr, srcr, scr, mask;
u32 strcr = 0, scr = 0, stcr, srcr, mask;
ssi->dai_fmt = fmt;
if (fsl_ssi_is_i2s_master(ssi) && IS_ERR(ssi->baudclk)) {
dev_err(dev, "missing baudclk for master mode\n");
return -EINVAL;
}
regmap_read(regs, REG_SSI_SCR, &scr);
scr &= ~(SSI_SCR_SYN | SSI_SCR_I2S_MODE_MASK); /* Synchronize frame sync clock for TE to avoid data slipping */ scr |= SSI_SCR_SYNC_TX_FS;
mask = SSI_STCR_TXBIT0 | SSI_STCR_TFDIR | SSI_STCR_TXDIR |
SSI_STCR_TSCKP | SSI_STCR_TFSI | SSI_STCR_TFSL | SSI_STCR_TEFS;
regmap_read(regs, REG_SSI_STCR, &stcr);
regmap_read(regs, REG_SSI_SRCR, &srcr);
stcr &= ~mask;
srcr &= ~mask;
/* Use Network mode as default */ ssi->i2s_net = SSI_SCR_NET; switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S:
regmap_update_bits(regs, REG_SSI_STCCR,
SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(2));
regmap_update_bits(regs, REG_SSI_SRCCR,
SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(2));
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFS: case SND_SOC_DAIFMT_CBS_CFS:
if (IS_ERR(ssi->baudclk)) {
dev_err(ssi->dev,
"missing baudclk for master mode\n");
return -EINVAL;
}
The original code did this check only for fsl_ssi_is_i2s_master(ssi), that is, only for SND_SOC_DAIFMT_CBS_CFS while here you also do it for SND_SOC_DAIFMT_CBM_CFS. Was this changed on purpose?
Maciej
On Sun, Jan 14, 2018 at 11:40:31PM +0100, Maciej S. Szmigiero wrote:
case SND_SOC_DAIFMT_I2S:
regmap_update_bits(regs, REG_SSI_STCCR,
SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(2));
regmap_update_bits(regs, REG_SSI_SRCCR,
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFS: case SND_SOC_DAIFMT_CBS_CFS:SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(2));
if (IS_ERR(ssi->baudclk)) {
dev_err(ssi->dev,
"missing baudclk for master mode\n");
return -EINVAL;
}
The original code did this check only for fsl_ssi_is_i2s_master(ssi), that is, only for SND_SOC_DAIFMT_CBS_CFS while here you also do it for SND_SOC_DAIFMT_CBM_CFS.
You are right. This patch isn't supposed to change that. I mixed an intention from another patch. Will revise this part in the v3.
Thanks a lot
The cpu_dai_drv is only used for symmetric_rates. So this patch replaces it with a synchronous boolean flag.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org --- sound/soc/fsl/fsl_ssi.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 213962a..716603c 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -208,11 +208,11 @@ struct fsl_ssi_soc_data { * * @regs: Pointer to the regmap registers * @irq: IRQ of this SSI - * @cpu_dai_drv: CPU DAI driver for this device * * @dai_fmt: DAI configuration this device is currently used with * @streams: Mask of current active streams: BIT(TX) and BIT(RX) * @i2s_net: I2S and Network mode configurations of SCR register + * @synchronous: Use synchronous mode - both of TX and RX use STCK and SFCK * @use_dma: DMA is used or FIQ with stream filter * @use_dual_fifo: DMA with support for dual FIFO mode * @has_ipg_clk_name: If "ipg" is in the clock name list of device tree @@ -253,11 +253,11 @@ struct fsl_ssi_soc_data { struct fsl_ssi { struct regmap *regs; int irq; - struct snd_soc_dai_driver cpu_dai_drv;
unsigned int dai_fmt; u8 streams; u8 i2s_net; + bool synchronous; bool use_dma; bool use_dual_fifo; bool has_ipg_clk_name; @@ -668,7 +668,6 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai); struct regmap *regs = ssi->regs; - int synchronous = ssi->cpu_dai_drv.symmetric_rates, ret; u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i; unsigned long clkrate, baudrate, tmprate; unsigned int slots = params_channels(hw_params); @@ -676,6 +675,7 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, u64 sub, savesub = 100000; unsigned int freq; bool baudclk_is_used; + int ret;
/* Override slots and slot_width if being specifically set... */ if (ssi->slots) @@ -754,7 +754,7 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, mask = SSI_SxCCR_PM_MASK | SSI_SxCCR_DIV2 | SSI_SxCCR_PSR;
/* STCCR is used for RX in synchronous mode */ - tx2 = tx || synchronous; + tx2 = tx || ssi->synchronous; regmap_update_bits(regs, REG_SSI_SxCCR(tx2), mask, stccr);
if (!baudclk_is_used) { @@ -802,7 +802,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, * that should set separate configurations for STCCR and SRCCR * despite running in the synchronous mode. */ - if (enabled && ssi->cpu_dai_drv.symmetric_rates) + if (enabled && ssi->synchronous) return 0;
if (fsl_ssi_is_i2s_master(ssi)) { @@ -834,7 +834,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, }
/* In synchronous mode, the SSI uses STCCR for capture */ - tx2 = tx || ssi->cpu_dai_drv.symmetric_rates; + tx2 = tx || ssi->synchronous; regmap_update_bits(regs, REG_SSI_SxCCR(tx2), SSI_SxCCR_WL_MASK, wl);
return 0; @@ -959,7 +959,7 @@ static int _fsl_ssi_set_dai_fmt(struct fsl_ssi *ssi, unsigned int fmt) srcr = strcr;
/* Set SYN mode and clear RXDIR bit when using SYN or AC97 mode */ - if (ssi->cpu_dai_drv.symmetric_rates || fsl_ssi_is_ac97(ssi)) { + if (ssi->synchronous || fsl_ssi_is_ac97(ssi)) { srcr &= ~SSI_SRCR_RXDIR; scr |= SSI_SCR_SYN; } @@ -1360,6 +1360,7 @@ static void fsl_ssi_imx_clean(struct platform_device *pdev, struct fsl_ssi *ssi)
static int fsl_ssi_probe(struct platform_device *pdev) { + struct snd_soc_dai_driver *cpu_dai_drv; struct fsl_ssi *ssi; int ret = 0; struct device_node *np = pdev->dev.of_node; @@ -1394,14 +1395,12 @@ static int fsl_ssi_probe(struct platform_device *pdev) ssi->use_dma = !of_property_read_bool(np, "fsl,fiq-stream-filter");
if (fsl_ssi_is_ac97(ssi)) { - memcpy(&ssi->cpu_dai_drv, &fsl_ssi_ac97_dai, - sizeof(fsl_ssi_ac97_dai)); + cpu_dai_drv = &fsl_ssi_ac97_dai; fsl_ac97_data = ssi; } else { - memcpy(&ssi->cpu_dai_drv, &fsl_ssi_dai_template, - sizeof(fsl_ssi_dai_template)); + cpu_dai_drv = &fsl_ssi_dai_template; } - ssi->cpu_dai_drv.name = dev_name(dev); + cpu_dai_drv->name = dev_name(dev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); iomem = devm_ioremap_resource(dev, res); @@ -1439,11 +1438,12 @@ static int fsl_ssi_probe(struct platform_device *pdev) /* Set software limitations for synchronous mode */ if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) { if (!fsl_ssi_is_ac97(ssi)) { - ssi->cpu_dai_drv.symmetric_rates = 1; - ssi->cpu_dai_drv.symmetric_samplebits = 1; + cpu_dai_drv->symmetric_rates = 1; + cpu_dai_drv->symmetric_samplebits = 1; + ssi->synchronous = true; }
- ssi->cpu_dai_drv.symmetric_channels = 1; + cpu_dai_drv->symmetric_channels = 1; }
/* Fetch FIFO depth; Set to 8 for older DT without this property */ @@ -1498,7 +1498,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) }
ret = devm_snd_soc_register_component(dev, &fsl_ssi_component, - &ssi->cpu_dai_drv, 1); + cpu_dai_drv, 1); if (ret) { dev_err(dev, "failed to register DAI: %d\n", ret); goto error_asoc_register;
On 11.01.2018 07:43, Nicolin Chen wrote:
The cpu_dai_drv is only used for symmetric_rates. So this patch replaces it with a synchronous boolean flag.
You make cpu_dai_drv common to all SSI instances instead of per-instance.
What if you have multiple SSIs in the system with different symmetric_{rates,samplebits,channels} settings?
Maciej
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org
sound/soc/fsl/fsl_ssi.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 213962a..716603c 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -208,11 +208,11 @@ struct fsl_ssi_soc_data {
- @regs: Pointer to the regmap registers
- @irq: IRQ of this SSI
- @cpu_dai_drv: CPU DAI driver for this device
- @dai_fmt: DAI configuration this device is currently used with
- @streams: Mask of current active streams: BIT(TX) and BIT(RX)
- @i2s_net: I2S and Network mode configurations of SCR register
- @synchronous: Use synchronous mode - both of TX and RX use STCK and SFCK
- @use_dma: DMA is used or FIQ with stream filter
- @use_dual_fifo: DMA with support for dual FIFO mode
- @has_ipg_clk_name: If "ipg" is in the clock name list of device tree
@@ -253,11 +253,11 @@ struct fsl_ssi_soc_data { struct fsl_ssi { struct regmap *regs; int irq;
struct snd_soc_dai_driver cpu_dai_drv;
unsigned int dai_fmt; u8 streams; u8 i2s_net;
- bool synchronous; bool use_dma; bool use_dual_fifo; bool has_ipg_clk_name;
@@ -668,7 +668,6 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai); struct regmap *regs = ssi->regs;
- int synchronous = ssi->cpu_dai_drv.symmetric_rates, ret; u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i; unsigned long clkrate, baudrate, tmprate; unsigned int slots = params_channels(hw_params);
@@ -676,6 +675,7 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, u64 sub, savesub = 100000; unsigned int freq; bool baudclk_is_used;
int ret;
/* Override slots and slot_width if being specifically set... */ if (ssi->slots)
@@ -754,7 +754,7 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, mask = SSI_SxCCR_PM_MASK | SSI_SxCCR_DIV2 | SSI_SxCCR_PSR;
/* STCCR is used for RX in synchronous mode */
- tx2 = tx || synchronous;
tx2 = tx || ssi->synchronous; regmap_update_bits(regs, REG_SSI_SxCCR(tx2), mask, stccr);
if (!baudclk_is_used) {
@@ -802,7 +802,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, * that should set separate configurations for STCCR and SRCCR * despite running in the synchronous mode. */
- if (enabled && ssi->cpu_dai_drv.symmetric_rates)
if (enabled && ssi->synchronous) return 0;
if (fsl_ssi_is_i2s_master(ssi)) {
@@ -834,7 +834,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, }
/* In synchronous mode, the SSI uses STCCR for capture */
- tx2 = tx || ssi->cpu_dai_drv.symmetric_rates;
tx2 = tx || ssi->synchronous; regmap_update_bits(regs, REG_SSI_SxCCR(tx2), SSI_SxCCR_WL_MASK, wl);
return 0;
@@ -959,7 +959,7 @@ static int _fsl_ssi_set_dai_fmt(struct fsl_ssi *ssi, unsigned int fmt) srcr = strcr;
/* Set SYN mode and clear RXDIR bit when using SYN or AC97 mode */
- if (ssi->cpu_dai_drv.symmetric_rates || fsl_ssi_is_ac97(ssi)) {
- if (ssi->synchronous || fsl_ssi_is_ac97(ssi)) { srcr &= ~SSI_SRCR_RXDIR; scr |= SSI_SCR_SYN; }
@@ -1360,6 +1360,7 @@ static void fsl_ssi_imx_clean(struct platform_device *pdev, struct fsl_ssi *ssi)
static int fsl_ssi_probe(struct platform_device *pdev) {
- struct snd_soc_dai_driver *cpu_dai_drv; struct fsl_ssi *ssi; int ret = 0; struct device_node *np = pdev->dev.of_node;
@@ -1394,14 +1395,12 @@ static int fsl_ssi_probe(struct platform_device *pdev) ssi->use_dma = !of_property_read_bool(np, "fsl,fiq-stream-filter");
if (fsl_ssi_is_ac97(ssi)) {
memcpy(&ssi->cpu_dai_drv, &fsl_ssi_ac97_dai,
sizeof(fsl_ssi_ac97_dai));
fsl_ac97_data = ssi; } else {cpu_dai_drv = &fsl_ssi_ac97_dai;
memcpy(&ssi->cpu_dai_drv, &fsl_ssi_dai_template,
sizeof(fsl_ssi_dai_template));
}cpu_dai_drv = &fsl_ssi_dai_template;
- ssi->cpu_dai_drv.name = dev_name(dev);
cpu_dai_drv->name = dev_name(dev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); iomem = devm_ioremap_resource(dev, res);
@@ -1439,11 +1438,12 @@ static int fsl_ssi_probe(struct platform_device *pdev) /* Set software limitations for synchronous mode */ if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) { if (!fsl_ssi_is_ac97(ssi)) {
ssi->cpu_dai_drv.symmetric_rates = 1;
ssi->cpu_dai_drv.symmetric_samplebits = 1;
cpu_dai_drv->symmetric_rates = 1;
cpu_dai_drv->symmetric_samplebits = 1;
}ssi->synchronous = true;
ssi->cpu_dai_drv.symmetric_channels = 1;
cpu_dai_drv->symmetric_channels = 1;
}
/* Fetch FIFO depth; Set to 8 for older DT without this property */
@@ -1498,7 +1498,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) }
ret = devm_snd_soc_register_component(dev, &fsl_ssi_component,
&ssi->cpu_dai_drv, 1);
if (ret) { dev_err(dev, "failed to register DAI: %d\n", ret); goto error_asoc_register;cpu_dai_drv, 1);
On Sun, Jan 14, 2018 at 11:42:59PM +0100, Maciej S. Szmigiero wrote:
On 11.01.2018 07:43, Nicolin Chen wrote:
The cpu_dai_drv is only used for symmetric_rates. So this patch replaces it with a synchronous boolean flag.
You make cpu_dai_drv common to all SSI instances instead of per-instance.
What if you have multiple SSIs in the system with different symmetric_{rates,samplebits,channels} settings?
Good catch...it should maintain each cpu_dai_drv separately. Thanks!
This patch cleans up probe() function by moving all Device Tree related code into a separate function. It allows the probe() to be Device Tree independent. This will be very useful for future integration of imx-ssi driver which has similar functionalities while exists only because it supports non-DT cases.
This patch also moves symmetric_channels of AC97 from the probe to the structure snd_soc_dai_driver for simplification.
Additionally, since PowerPC and AC97 use the same pdev pointer to register a platform device, this patch also unifies related code.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org --- sound/soc/fsl/fsl_ssi.c | 204 +++++++++++++++++++++++++----------------------- 1 file changed, 108 insertions(+), 96 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 716603c..0754eeb 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -234,8 +234,12 @@ struct fsl_ssi_soc_data { * * @fiq_params: FIQ stream filtering parameters * - * @pdev: Pointer to pdev when using fsl-ssi as sound card (ppc only) - * TODO: Should be replaced with simple-sound-card + * @card_pdev: Platform_device pointer when using fsl-ssi as sound card + * (PowerPC or AC97 only) + * @card_name: Platform_device name when using fsl-ssi as sound card + * (PowerPC or AC97 only) + * @card_idx: The index of SSI when registering a sound card + * (PowerPC or AC97 only) * * @dbg_stats: Debugging statistics * @@ -279,7 +283,9 @@ struct fsl_ssi {
struct imx_pcm_fiq_params fiq_params;
- struct platform_device *pdev; + struct platform_device *card_pdev; + char card_name[32]; + u32 card_idx;
struct fsl_ssi_dbg dbg_stats;
@@ -1123,6 +1129,7 @@ static const struct snd_soc_component_driver fsl_ssi_component = {
static struct snd_soc_dai_driver fsl_ssi_ac97_dai = { .bus_control = true, + .symmetric_channels = 1, .probe = fsl_ssi_dai_probe, .playback = { .stream_name = "AC97 Playback", @@ -1274,9 +1281,7 @@ static void make_lowercase(char *s) static int fsl_ssi_imx_probe(struct platform_device *pdev, struct fsl_ssi *ssi, void __iomem *iomem) { - struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; - u32 dmas[4]; int ret;
/* Backward compatible for a DT without ipg clock name assigned */ @@ -1310,14 +1315,8 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev, ssi->dma_params_tx.addr = ssi->ssi_phys + REG_SSI_STX0; ssi->dma_params_rx.addr = ssi->ssi_phys + REG_SSI_SRX0;
- /* Set to dual FIFO mode according to the SDMA sciprt */ - ret = of_property_read_u32_array(np, "dmas", dmas, 4); - if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL) { - ssi->use_dual_fifo = true; - /* - * Use even numbers to avoid channel swap due to SDMA - * script design - */ + /* Use even numbers to avoid channel swap due to SDMA script design */ + if (ssi->use_dual_fifo) { ssi->dma_params_tx.maxburst &= ~0x1; ssi->dma_params_rx.maxburst &= ~0x1; } @@ -1358,42 +1357,103 @@ static void fsl_ssi_imx_clean(struct platform_device *pdev, struct fsl_ssi *ssi) clk_disable_unprepare(ssi->clk); }
-static int fsl_ssi_probe(struct platform_device *pdev) +static int fsl_ssi_probe_from_dt(struct fsl_ssi *ssi) { - struct snd_soc_dai_driver *cpu_dai_drv; - struct fsl_ssi *ssi; - int ret = 0; - struct device_node *np = pdev->dev.of_node; - struct device *dev = &pdev->dev; + struct device *dev = ssi->dev; + struct device_node *np = dev->of_node; const struct of_device_id *of_id; const char *p, *sprop; const uint32_t *iprop; - struct resource *res; - void __iomem *iomem; - char name[64]; - struct regmap_config regconfig = fsl_ssi_regconfig; + u32 dmas[4]; + int ret;
of_id = of_match_device(fsl_ssi_ids, dev); if (!of_id || !of_id->data) return -EINVAL;
- ssi = devm_kzalloc(dev, sizeof(*ssi), GFP_KERNEL); - if (!ssi) - return -ENOMEM; - ssi->soc = of_id->data; - ssi->dev = dev; + + ret = of_property_match_string(np, "clock-names", "ipg"); + /* Get error code if not found */ + ssi->has_ipg_clk_name = ret >= 0;
/* Check if being used in AC97 mode */ sprop = of_get_property(np, "fsl,mode", NULL); - if (sprop) { - if (!strcmp(sprop, "ac97-slave")) - ssi->dai_fmt = FSLSSI_AC97_DAIFMT; + if (sprop && !strcmp(sprop, "ac97-slave")) { + ssi->dai_fmt = FSLSSI_AC97_DAIFMT; + + ret = of_property_read_u32(np, "cell-index", &ssi->card_idx); + if (ret) { + dev_err(dev, "failed to get SSI index property\n"); + return -EINVAL; + } + strcpy(ssi->card_name, "ac97-codec"); }
/* Select DMA or FIQ */ ssi->use_dma = !of_property_read_bool(np, "fsl,fiq-stream-filter");
+ /* In synchronous mode, STCK and STFS ports are used by RX as well */ + if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) + ssi->synchronous = true; + + /* Fetch FIFO depth; Set to 8 for older DT without this property */ + iprop = of_get_property(np, "fsl,fifo-depth", NULL); + if (iprop) + ssi->fifo_depth = be32_to_cpup(iprop); + else + ssi->fifo_depth = 8; + + /* Use dual FIFO mode depending on the support from SDMA script */ + ret = of_property_read_u32_array(np, "dmas", dmas, 4); + if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL) + ssi->use_dual_fifo = true; + + /* + * Backward compatible for older bindings by manually triggering the + * machine driver's probe(). Use /compatible property, including the + * address of CPU DAI driver structure, as the name of machine driver + * + * If card_name is set by AC97 earlier, bypass here since it uses a + * different name to register the device. + */ + if (!ssi->card_name[0] && of_get_property(np, "codec-handle", NULL)) { + sprop = of_get_property(of_find_node_by_path("/"), + "compatible", NULL); + /* Strip "fsl," in the compatible name if applicable */ + p = strrchr(sprop, ','); + if (p) + sprop = p + 1; + snprintf(ssi->card_name, sizeof(ssi->card_name), + "snd-soc-%s", sprop); + make_lowercase(ssi->card_name); + ssi->card_idx = 0; + } + + return 0; +} + +static int fsl_ssi_probe(struct platform_device *pdev) +{ + struct regmap_config regconfig = fsl_ssi_regconfig; + struct snd_soc_dai_driver *cpu_dai_drv; + struct device *dev = &pdev->dev; + struct fsl_ssi *ssi; + struct resource *res; + void __iomem *iomem; + int ret = 0; + + ssi = devm_kzalloc(dev, sizeof(*ssi), GFP_KERNEL); + if (!ssi) + return -ENOMEM; + + ssi->dev = dev; + + /* Probe from DT */ + ret = fsl_ssi_probe_from_dt(ssi); + if (ret) + return ret; + if (fsl_ssi_is_ac97(ssi)) { cpu_dai_drv = &fsl_ssi_ac97_dai; fsl_ac97_data = ssi; @@ -1415,15 +1475,11 @@ static int fsl_ssi_probe(struct platform_device *pdev) REG_SSI_SRMSK / sizeof(uint32_t) + 1; }
- ret = of_property_match_string(np, "clock-names", "ipg"); - if (ret < 0) { - ssi->has_ipg_clk_name = false; - ssi->regs = devm_regmap_init_mmio(dev, iomem, ®config); - } else { - ssi->has_ipg_clk_name = true; + if (ssi->has_ipg_clk_name) ssi->regs = devm_regmap_init_mmio_clk(dev, "ipg", iomem, ®config); - } + else + ssi->regs = devm_regmap_init_mmio(dev, iomem, ®config); if (IS_ERR(ssi->regs)) { dev_err(dev, "failed to init register map\n"); return PTR_ERR(ssi->regs); @@ -1435,24 +1491,13 @@ static int fsl_ssi_probe(struct platform_device *pdev) return ssi->irq; }
- /* Set software limitations for synchronous mode */ - if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) { - if (!fsl_ssi_is_ac97(ssi)) { - cpu_dai_drv->symmetric_rates = 1; - cpu_dai_drv->symmetric_samplebits = 1; - ssi->synchronous = true; - } - + /* Set software limitations for synchronous mode except AC97 */ + if (ssi->synchronous && !fsl_ssi_is_ac97(ssi)) { + cpu_dai_drv->symmetric_rates = 1; cpu_dai_drv->symmetric_channels = 1; + cpu_dai_drv->symmetric_samplebits = 1; }
- /* Fetch FIFO depth; Set to 8 for older DT without this property */ - iprop = of_get_property(np, "fsl,fifo-depth", NULL); - if (iprop) - ssi->fifo_depth = be32_to_cpup(iprop); - else - ssi->fifo_depth = 8; - /* * Configure TX and RX DMA watermarks -- when to send a DMA request * @@ -1517,50 +1562,17 @@ static int fsl_ssi_probe(struct platform_device *pdev) if (ret) goto error_asoc_register;
- /* Bypass it if using newer DT bindings of ASoC machine drivers */ - if (!of_get_property(np, "codec-handle", NULL)) - goto done; - - /* - * Backward compatible for older bindings by manually triggering the - * machine driver's probe(). Use /compatible property, including the - * address of CPU DAI driver structure, as the name of machine driver. - */ - sprop = of_get_property(of_find_node_by_path("/"), "compatible", NULL); - /* Sometimes the compatible name has a "fsl," prefix, so we strip it. */ - p = strrchr(sprop, ','); - if (p) - sprop = p + 1; - snprintf(name, sizeof(name), "snd-soc-%s", sprop); - make_lowercase(name); - - ssi->pdev = platform_device_register_data(dev, name, 0, NULL, 0); - if (IS_ERR(ssi->pdev)) { - ret = PTR_ERR(ssi->pdev); - dev_err(dev, "failed to register platform: %d\n", ret); - goto error_sound_card; - } - -done: /* Initially configures SSI registers */ fsl_ssi_hw_init(ssi);
- if (fsl_ssi_is_ac97(ssi)) { - u32 ssi_idx; - - ret = of_property_read_u32(np, "cell-index", &ssi_idx); - if (ret) { - dev_err(dev, "failed to get SSI index property\n"); - goto error_sound_card; - } - - ssi->pdev = platform_device_register_data(NULL, "ac97-codec", - ssi_idx, NULL, 0); - if (IS_ERR(ssi->pdev)) { - ret = PTR_ERR(ssi->pdev); - dev_err(dev, - "failed to register AC97 codec platform: %d\n", - ret); + /* Register a platform device for older bindings or AC97 */ + if (ssi->card_name[0]) { + ssi->card_pdev = platform_device_register_data(dev, + ssi->card_name, ssi->card_idx, NULL, 0); + if (IS_ERR(ssi->card_pdev)) { + ret = PTR_ERR(ssi->card_pdev); + dev_err(dev, "failed to register %s: %d\n", + ssi->card_name, ret); goto error_sound_card; } } @@ -1590,8 +1602,8 @@ static int fsl_ssi_remove(struct platform_device *pdev)
fsl_ssi_debugfs_remove(&ssi->dbg_stats);
- if (ssi->pdev) - platform_device_unregister(ssi->pdev); + if (ssi->card_pdev) + platform_device_unregister(ssi->card_pdev);
if (ssi->soc->imx) fsl_ssi_imx_clean(pdev, ssi);
Since ssi->streams is being updated along with SCR register and its SSIEN bit, it's simpler to use it instead.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org --- sound/soc/fsl/fsl_ssi.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 0754eeb..52ddce1 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -796,11 +796,6 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, unsigned int sample_size = params_width(hw_params); u32 wl = SSI_SxCCR_WL(sample_size); int ret; - u32 scr; - int enabled; - - regmap_read(regs, REG_SSI_SCR, &scr); - enabled = scr & SSI_SCR_SSIEN;
/* * SSI is properly configured if it is enabled and running in @@ -808,7 +803,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, * that should set separate configurations for STCCR and SRCCR * despite running in the synchronous mode. */ - if (enabled && ssi->synchronous) + if (ssi->streams && ssi->synchronous) return 0;
if (fsl_ssi_is_i2s_master(ssi)) {
The patch
ASoC: fsl_ssi: Use ssi->streams instead of reading register
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 9c4f509a53cbcd968677c7f15c07260dd6c2276c Mon Sep 17 00:00:00 2001
From: Nicolin Chen nicoleotsuka@gmail.com Date: Mon, 12 Feb 2018 14:03:25 -0800 Subject: [PATCH] ASoC: fsl_ssi: Use ssi->streams instead of reading register
Since ssi->streams is being updated along with SCR register and its SSIEN bit, it's simpler to use it instead.
Signed-off-by: Nicolin Chen nicoleotsuka@gmail.com Tested-by: Caleb Crome caleb@crome.org Tested-by: Maciej S. Szmigiero mail@maciej.szmigiero.name Reviewed-by: Maciej S. Szmigiero mail@maciej.szmigiero.name Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/fsl/fsl_ssi.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 5bc67ad8000f..0823b08923b5 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -803,11 +803,6 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, unsigned int sample_size = params_width(hw_params); u32 wl = SSI_SxCCR_WL(sample_size); int ret; - u32 scr; - int enabled; - - regmap_read(regs, REG_SSI_SCR, &scr); - enabled = scr & SSI_SCR_SSIEN;
/* * SSI is properly configured if it is enabled and running in @@ -815,7 +810,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, * that should set separate configurations for STCCR and SRCCR * despite running in the synchronous mode. */ - if (enabled && ssi->synchronous) + if (ssi->streams && ssi->synchronous) return 0;
if (fsl_ssi_is_i2s_master(ssi)) {
On Wed, Jan 10, 2018 at 10:42 PM, Nicolin Chen nicoleotsuka@gmail.com wrote:
==Change log== v2
- Reworked the series by taking suggestions from Maciej
- Added PATCH-01 to keep all ssi->i2s_net updated
- Replaced bool tx with bool dir in PATCH-03 and PATCH-06
- Moved all initial register configurations from dai probe() to platform probe() so as to let AC97 CODEC successfully probe.
- Added Tested-by from Caleb for TDM test cases.
==Background== The fsl_ssi driver was designed for PPC originally and then it has been updated to support different modes for i.MX Series, including SDMA, I2S Master mode, AC97 and older i.MXs with FIQ, by different contributors for different use cases in different coding styles.
Additionally, in order to fix/work-around hardware bugs and design flaws, the driver made a lot of compromise so now its program flow looks very complicated and it's getting hard to maintain or update.
So I am going to clean up the driver on both coding style level and program flow level.
==Introduction== This series of patches is the second set to clean up fsl_ssi driver in the program flow level. Any patch here may impact a fundamental test case like playback or record.
==Verification== This series of patches require fully tested. I have done such tests on i.MX6SoloX with WM8962 using imx_v6_v7_defconfig as:
- Playback via I2S Master and Slave mode
- Record via I2S Master and Slave mode
- Simultaneous playback and record via I2S Master and Slave mode
- Background playback with foreground record (starting at different time) via I2S Master and Slave mode
- Background record with foreground playback (starting at different time) via I2S Master and Slave mode
- All tests above by hacking offline_config to true in imx51.
Caleb has tested v1 with TDM lookback tests on i.MX6.
Example of uncovered tests: AC97, PowerPC and FIQ.
Nicolin Chen (16): ASoC: fsl_ssi: Keep ssi->i2s_net updated ASoC: fsl_ssi: Clean up set_dai_tdm_slot() ASoC: fsl_ssi: Maintain a mask of active streams ASoC: fsl_ssi: Rename fsl_ssi_disable_val macro ASoC: fsl_ssi: Clear FIFO directly in fsl_ssi_config() ASoC: fsl_ssi: Clean up helper functions of trigger() ASoC: fsl_ssi: Add DAIFMT define for AC97 ASoC: fsl_ssi: Clean up fsl_ssi_setup_regvals() ASoC: fsl_ssi: Set xFEN0 and xFEN1 together ASoC: fsl_ssi: Use snd_soc_init_dma_data instead ASoC: fsl_ssi: Move one-time configurations to probe() ASoC: fsl_ssi: Setup AC97 in fsl_ssi_hw_init() ASoC: fsl_ssi: Clean up _fsl_ssi_set_dai_fmt() ASoC: fsl_ssi: Remove cpu_dai_drv from fsl_ssi structure ASoC: fsl_ssi: Move DT related code to a separate probe() ASoC: fsl_ssi: Use ssi->streams instead of reading register
sound/soc/fsl/fsl_ssi.c | 740 ++++++++++++++++++++++++------------------------ 1 file changed, 369 insertions(+), 371 deletions(-)
-- 2.7.4
Tested again, just to be sure... All looks good.
Tested-by: Caleb Crome caleb@crome.org
participants (4)
-
Caleb Crome
-
Maciej S. Szmigiero
-
Mark Brown
-
Nicolin Chen