lkml.org 
[lkml]   [2020]   [Jul]   [10]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    /
    From
    SubjectRe: [linux-sunxi] [PATCH 01/16] ASoC: sun4i-i2s: Add support for H6 I2S
    Date
    On 7/4/20 6:38 AM, Clément Péron wrote:
    > From: Jernej Skrabec <jernej.skrabec@siol.net>
    >
    > H6 I2S is very similar to that in H3, except it supports up to 16
    > channels.
    >
    > Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
    > Signed-off-by: Marcus Cooper <codekipper@gmail.com>
    > Signed-off-by: Clément Péron <peron.clem@gmail.com>
    > ---
    > sound/soc/sunxi/sun4i-i2s.c | 227 ++++++++++++++++++++++++++++++++++++
    > 1 file changed, 227 insertions(+)
    >
    > diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
    > index d0a8d5810c0a..9690389cb68e 100644
    > --- a/sound/soc/sunxi/sun4i-i2s.c
    > +++ b/sound/soc/sunxi/sun4i-i2s.c
    > @@ -124,6 +124,21 @@
    > #define SUN8I_I2S_RX_CHAN_SEL_REG 0x54
    > #define SUN8I_I2S_RX_CHAN_MAP_REG 0x58
    >
    > +/* Defines required for sun50i-h6 support */
    > +#define SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK GENMASK(21, 20)
    > +#define SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(offset) ((offset) << 20)
    > +#define SUN50I_H6_I2S_TX_CHAN_SEL_MASK GENMASK(19, 16)
    > +#define SUN50I_H6_I2S_TX_CHAN_SEL(chan) ((chan - 1) << 16)
    > +#define SUN50I_H6_I2S_TX_CHAN_EN_MASK GENMASK(15, 0)
    > +#define SUN50I_H6_I2S_TX_CHAN_EN(num_chan) (((1 << num_chan) - 1))
    > +
    > +#define SUN50I_H6_I2S_TX_CHAN_MAP0_REG 0x44
    > +#define SUN50I_H6_I2S_TX_CHAN_MAP1_REG 0x48
    > +
    > +#define SUN50I_H6_I2S_RX_CHAN_SEL_REG 0x64
    > +#define SUN50I_H6_I2S_RX_CHAN_MAP0_REG 0x68
    > +#define SUN50I_H6_I2S_RX_CHAN_MAP1_REG 0x6C
    > +
    > struct sun4i_i2s;
    >
    > /**
    > @@ -466,6 +481,65 @@ static int sun8i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s,
    > return 0;
    > }
    >
    > +static int sun50i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s,
    > + const struct snd_pcm_hw_params *params)
    > +{
    > + unsigned int channels = params_channels(params);
    > + unsigned int slots = channels;
    > + unsigned int lrck_period;
    > +
    > + if (i2s->slots)
    > + slots = i2s->slots;
    > +
    > + /* Map the channels for playback and capture */
    > + regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP1_REG, 0x76543210);
    > + regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP1_REG, 0x76543210);
    > +
    > + /* Configure the channels */
    > + regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
    > + SUN50I_H6_I2S_TX_CHAN_SEL_MASK,
    > + SUN50I_H6_I2S_TX_CHAN_SEL(channels));
    > + regmap_update_bits(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_SEL_REG,
    > + SUN50I_H6_I2S_TX_CHAN_SEL_MASK,
    > + SUN50I_H6_I2S_TX_CHAN_SEL(channels));
    > +
    > + regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
    > + SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK,
    > + SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(channels));
    > + regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
    > + SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK,
    > + SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(channels));
    > +
    > + switch (i2s->format & SND_SOC_DAIFMT_FORMAT_MASK) {
    > + case SND_SOC_DAIFMT_DSP_A:
    > + case SND_SOC_DAIFMT_DSP_B:
    > + case SND_SOC_DAIFMT_LEFT_J:
    > + case SND_SOC_DAIFMT_RIGHT_J:

    According to the manual, LEFT_J and RIGHT_J should use the same calculation as
    I2S, not the one for PCM/DSP.

    > + lrck_period = params_physical_width(params) * slots;
    > + break;
    > +
    > + case SND_SOC_DAIFMT_I2S:
    > + lrck_period = params_physical_width(params);
    > + break;
    > +
    > + default:
    > + return -EINVAL;
    > + }
    > +
    > + if (i2s->slot_width)
    > + lrck_period = i2s->slot_width;
    > +
    > + regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
    > + SUN8I_I2S_FMT0_LRCK_PERIOD_MASK,
    > + SUN8I_I2S_FMT0_LRCK_PERIOD(lrck_period));

    From the description in the manual, this looks off by one. The number of BCLKs
    per LRCK is LRCK_PERIOD + 1.

    > +
    > + regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
    > + SUN50I_H6_I2S_TX_CHAN_EN_MASK,
    > + SUN50I_H6_I2S_TX_CHAN_EN(channels));
    > +
    > + return 0;
    > +}
    > +
    > static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
    > struct snd_pcm_hw_params *params,
    > struct snd_soc_dai *dai)
    > @@ -691,6 +765,108 @@ static int sun8i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
    > return 0;
    > }
    >
    > +static int sun50i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
    > + unsigned int fmt)
    > +{
    > + u32 mode, val;
    > + u8 offset;
    > +
    > + /*
    > + * DAI clock polarity
    > + *
    > + * The setup for LRCK contradicts the datasheet, but under a
    > + * scope it's clear that the LRCK polarity is reversed
    > + * compared to the expected polarity on the bus.
    > + */

    This comment makes us sound a lot more confident than I think we actually are.

    Regards,
    Samuel

    \
     
     \ /
      Last update: 2020-07-10 07:44    [W:3.107 / U:0.040 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site