From b97a67a14e5c5904b84e52150e0d13da794fc9d7 Mon Sep 17 00:00:00 2001 From: gtrainavicius Date: Tue, 10 Jan 2017 21:59:39 +0200 Subject: [PATCH] bcm2835-i2s: Changes for allowing asymmetric sample formats. (#1783) This is achieved by making changes only to the requested stream direction format, keeping the other stream direction configuration intact. Signed-off-by: Giedrius Trainavicius --- sound/soc/bcm/bcm2835-i2s.c | 54 +++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c index aedb01f..d2b0801 100644 --- a/sound/soc/bcm/bcm2835-i2s.c +++ b/sound/soc/bcm/bcm2835-i2s.c @@ -310,6 +310,7 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, unsigned int sampling_rate = params_rate(params); unsigned int data_length, data_delay, bclk_ratio; unsigned int ch1pos, ch2pos, mode, format; + unsigned int previous_ftxp, previous_frxp; unsigned int mash = BCM2835_CLK_MASH_1; unsigned int divi, divf, target_frequency; int clk_src = -1; @@ -320,6 +321,7 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, bool frame_master = (master == SND_SOC_DAIFMT_CBS_CFS || master == SND_SOC_DAIFMT_CBM_CFS); uint32_t csreg; + bool packed; /* * If a stream is already enabled, @@ -465,26 +467,46 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - /* - * Set format for both streams. - * We cannot set another frame length - * (and therefore word length) anyway, - * so the format will be the same. - */ - regmap_write(dev->i2s_regmap, BCM2835_I2S_RXC_A_REG, format); - regmap_write(dev->i2s_regmap, BCM2835_I2S_TXC_A_REG, format); + /* Set the format for the matching stream direction. */ + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + regmap_write(dev->i2s_regmap, BCM2835_I2S_TXC_A_REG, format); + break; + case SNDRV_PCM_STREAM_CAPTURE: + regmap_write(dev->i2s_regmap, BCM2835_I2S_RXC_A_REG, format); + break; + default: + return -EINVAL; + } /* Setup the I2S mode */ + /* Keep existing FTXP and FRXP values. */ + regmap_read(dev->i2s_regmap, BCM2835_I2S_MODE_A_REG, &mode); + + previous_ftxp = mode & BCM2835_I2S_FTXP; + previous_frxp = mode & BCM2835_I2S_FRXP; + mode = 0; - if (data_length <= 16) { - /* - * Use frame packed mode (2 channels per 32 bit word) - * We cannot set another frame length in the second stream - * (and therefore word length) anyway, - * so the format will be the same. - */ - mode |= BCM2835_I2S_FTXP | BCM2835_I2S_FRXP; + /* + * Retain the frame packed mode (2 channels per 32 bit word) + * of the other direction stream intact. The formats of each + * direction can be different as long as the frame length is + * shared for both. + */ + packed = data_length <= 16; + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + mode |= previous_frxp; + mode |= packed ? BCM2835_I2S_FTXP : 0; + break; + case SNDRV_PCM_STREAM_CAPTURE: + mode |= previous_ftxp; + mode |= packed ? BCM2835_I2S_FRXP : 0; + break; + default: + return -EINVAL; } mode |= BCM2835_I2S_FLEN(bclk_ratio - 1); -- 2.1.4