2020-10-01 02:15:35

by Samuel Holland

[permalink] [raw]
Subject: [PATCH 00/25] ASoC: sun8i-codec: support for AIF2 and AIF3

This series adds support the other two AIFs present in the sun8i codec,
which can be used for codec2codec DAI links.

This series first cleans up the DAPM component driver so there is an
organized place to put the new widgets. Then it fills out the DAI
driver, removing assumptions that were made for AIF1 (16 bits, 2
channels, certain clock inversions). Some new logic is required to
handle 3 DAIs and the ADC/DAC sharing the same clock. Finally, it adds
the new DAIs, and hooks them up with DAPM widgets and routes per the
hardware topology.

To minimize the number of patches in this series, related device tree
patches (increasing #sound-dai-cells, adding new DAI links) will be sent
separately.

Samuel Holland (25):
ASoC: sun8i-codec: Set up clock tree at probe time
ASoC: sun8i-codec: Swap module clock/reset dependencies
ASoC: sun8i-codec: Sort DAPM controls, widgets, and routes
ASoC: sun8i-codec: Consistently name DAPM widgets and routes
ASoC: sun8i-codec: Correct DAPM widget types
ASoC: sun8i-codec: Fix AIF widget channel references
ASoC: sun8i-codec: Enable AIF mono/stereo control
ASoC: sun8i-codec: Use snd_soc_dai_get_drvdata
ASoC: sun8i-codec: Prepare to extend the DAI driver
ASoC: sun8i-codec: Program format before clock inversion
ASoC: sun8i-codec: Enable all supported clock inversions
ASoC: sun8i-codec: Program the correct word size
ASoC: sun8i-codec: Round up the LRCK divisor
ASoC: sun8i-codec: Correct the BCLK divisor calculation
ASoC: sun8i-codec: Support the TDM slot binding
ASoC: sun8i-codec: Enforce symmetric DAI parameters
ASoC: sun8i-codec: Enable all supported sample rates
ASoC: sun8i-codec: Automatically set the system sample rate
ASoC: sun8i-codec: Constrain to compatible sample rates
ASoC: sun8i-codec: Protect the clock rate while streams are open
ASoC: sun8i-codec: Require an exact BCLK divisor match
ASoC: sun8i-codec: Enable all supported PCM formats
ASoC: sun8i-codec: Generalize AIF clock control
ASoC: sun8i-codec: Add a DAI, widgets, and routes for AIF2
ASoC: sun8i-codec: Add a DAI, widgets, and routes for AIF3

sound/soc/sunxi/sun8i-codec.c | 1135 ++++++++++++++++++++++++++-------
1 file changed, 894 insertions(+), 241 deletions(-)

--
2.26.2


2020-10-01 02:15:53

by Samuel Holland

[permalink] [raw]
Subject: [PATCH 17/25] ASoC: sun8i-codec: Enable all supported sample rates

The system sample rate programmed into the hardware is really a clock
divider from SYSCLK to the ADC and DAC. Since we support two SYSCLK
frequencies, we can use all sample rates corresponding to one of those
frequencies divided by any available divisor.

This commit enables support for those sample rates. It also stops
advertising support for a 64 kHz sample rate, which is not supported.

Signed-off-by: Samuel Holland <[email protected]>
---
sound/soc/sunxi/sun8i-codec.c | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
index f21274530a0e..f4b2a7cc9810 100644
--- a/sound/soc/sunxi/sun8i-codec.c
+++ b/sound/soc/sunxi/sun8i-codec.c
@@ -89,16 +89,23 @@
#define SUN8I_SYS_SR_CTRL_AIF1_FS_MASK GENMASK(15, 12)
#define SUN8I_SYS_SR_CTRL_AIF2_FS_MASK GENMASK(11, 8)
#define SUN8I_AIF1CLK_CTRL_AIF1_CLK_INV_MASK GENMASK(14, 13)
#define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK GENMASK(12, 9)
#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK GENMASK(8, 6)
#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK GENMASK(5, 4)
#define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK GENMASK(3, 2)

+#define SUN8I_CODEC_PCM_RATES (SNDRV_PCM_RATE_8000_48000|\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000 |\
+ SNDRV_PCM_RATE_KNOT)
+
enum {
AIF1,
NAIFS
};

struct sun8i_codec_aif {
unsigned int slots;
unsigned int slot_width;
@@ -142,37 +149,41 @@ static int sun8i_codec_runtime_suspend(struct device *dev)
return 0;
}

static int sun8i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
{
unsigned int rate = params_rate(params);

switch (rate) {
- case 8000:
case 7350:
+ case 8000:
return 0x0;
case 11025:
return 0x1;
case 12000:
return 0x2;
+ case 14700:
case 16000:
return 0x3;
case 22050:
return 0x4;
case 24000:
return 0x5;
+ case 29400:
case 32000:
return 0x6;
case 44100:
return 0x7;
case 48000:
return 0x8;
+ case 88200:
case 96000:
return 0x9;
+ case 176400:
case 192000:
return 0xa;
default:
return -EINVAL;
}
}

static int sun8i_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
@@ -408,26 +419,26 @@ static struct snd_soc_dai_driver sun8i_codec_dais[] = {
.name = "sun8i-codec-aif1",
.id = AIF1,
.ops = &sun8i_codec_dai_ops,
/* capture capabilities */
.capture = {
.stream_name = "AIF1 Capture",
.channels_min = 1,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .rates = SUN8I_CODEC_PCM_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.sig_bits = 24,
},
/* playback capabilities */
.playback = {
.stream_name = "AIF1 Playback",
.channels_min = 1,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .rates = SUN8I_CODEC_PCM_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.symmetric_rates = true,
.symmetric_channels = true,
.symmetric_samplebits = true,
},
};

--
2.26.2

2020-10-01 02:15:55

by Samuel Holland

[permalink] [raw]
Subject: [PATCH 06/25] ASoC: sun8i-codec: Fix AIF widget channel references

Both the left and right side widgets referenced channel 0. This would
unnecessarily power on the right side widget (and its associated path)
when a mono stream was active.

Signed-off-by: Samuel Holland <[email protected]>
---
sound/soc/sunxi/sun8i-codec.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
index d0028883950c..2c89974243e1 100644
--- a/sound/soc/sunxi/sun8i-codec.c
+++ b/sound/soc/sunxi/sun8i-codec.c
@@ -408,31 +408,31 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("DAC",
SUN8I_DAC_DIG_CTRL,
SUN8I_DAC_DIG_CTRL_ENDA, 0, NULL, 0),

/* AIF "ADC" Outputs */
SND_SOC_DAPM_AIF_OUT("AIF1 AD0L", "Capture", 0,
SUN8I_AIF1_ADCDAT_CTRL,
SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA, 0),
- SND_SOC_DAPM_AIF_OUT("AIF1 AD0R", "Capture", 0,
+ SND_SOC_DAPM_AIF_OUT("AIF1 AD0R", "Capture", 1,
SUN8I_AIF1_ADCDAT_CTRL,
SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA, 0),

/* AIF "ADC" Mixers */
SOC_MIXER_ARRAY("AIF1 AD0L Mixer", SND_SOC_NOPM, 0, 0,
sun8i_aif1_ad0_mixer_controls),
SOC_MIXER_ARRAY("AIF1 AD0R Mixer", SND_SOC_NOPM, 0, 0,
sun8i_aif1_ad0_mixer_controls),

/* AIF "DAC" Inputs */
SND_SOC_DAPM_AIF_IN("AIF1 DA0L", "Playback", 0,
SUN8I_AIF1_DACDAT_CTRL,
SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0),
- SND_SOC_DAPM_AIF_IN("AIF1 DA0R", "Playback", 0,
+ SND_SOC_DAPM_AIF_IN("AIF1 DA0R", "Playback", 1,
SUN8I_AIF1_DACDAT_CTRL,
SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0),

/* ADC Inputs (connected to analog codec DAPM context) */
SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),

/* DAC Outputs (connected to analog codec DAPM context) */
--
2.26.2

2020-10-01 02:16:42

by Samuel Holland

[permalink] [raw]
Subject: [PATCH 10/25] ASoC: sun8i-codec: Program format before clock inversion

The LRCK inversion bit has a different meaning in DSP mode: it selects
between DSP A and DSP B formats. To support this, we need to know if
the selected format is a DSP format. One easy way to do this is to set
the format field before the inversion fields.

Signed-off-by: Samuel Holland <[email protected]>
---
sound/soc/sunxi/sun8i-codec.c | 46 +++++++++++++++++------------------
1 file changed, 23 insertions(+), 23 deletions(-)

diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
index 346f699c2e86..0b713b2a2028 100644
--- a/sound/soc/sunxi/sun8i-codec.c
+++ b/sound/soc/sunxi/sun8i-codec.c
@@ -168,33 +168,55 @@ static int sun8i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
default:
return -EINVAL;
}
}

static int sun8i_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
- u32 value;
+ u32 format, value;

/* clock masters */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS: /* Codec slave, DAI master */
value = 0x1;
break;
case SND_SOC_DAIFMT_CBM_CFM: /* Codec Master, DAI slave */
value = 0x0;
break;
default:
return -EINVAL;
}
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
BIT(SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD),
value << SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD);

+ /* DAI format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = 0x0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = 0x1;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ format = 0x2;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ format = 0x3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
+ SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK,
+ format << SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT);
+
/* clock inversion */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF: /* Normal */
value = 0x0;
break;
case SND_SOC_DAIFMT_IB_IF: /* Inversion */
value = 0x1;
break;
@@ -215,38 +237,16 @@ static int sun8i_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
* that the codec probably gets it backward, and we have to
* invert the value here.
*/
value ^= scodec->quirks->lrck_inversion;
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
BIT(SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV),
value << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV);

- /* DAI format */
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- value = 0x0;
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- value = 0x1;
- break;
- case SND_SOC_DAIFMT_RIGHT_J:
- value = 0x2;
- break;
- case SND_SOC_DAIFMT_DSP_A:
- case SND_SOC_DAIFMT_DSP_B:
- value = 0x3;
- break;
- default:
- return -EINVAL;
- }
- regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
- SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK,
- value << SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT);
-
return 0;
}

struct sun8i_codec_clk_div {
u8 div;
u8 val;
};

--
2.26.2

2020-10-01 02:16:46

by Samuel Holland

[permalink] [raw]
Subject: [PATCH 19/25] ASoC: sun8i-codec: Constrain to compatible sample rates

While another stream is active, only allow userspace to use sample rates
that are compatible with the current SYSCLK frequency. This ensures the
actual sample rate will always match what is given in hw_params.

Signed-off-by: Samuel Holland <[email protected]>
---
sound/soc/sunxi/sun8i-codec.c | 59 ++++++++++++++++++++++++++++++++---
1 file changed, 55 insertions(+), 4 deletions(-)

diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
index 43b4319e3d84..501af64d43a0 100644
--- a/sound/soc/sunxi/sun8i-codec.c
+++ b/sound/soc/sunxi/sun8i-codec.c
@@ -121,16 +121,18 @@ struct sun8i_codec_quirks {
bool lrck_inversion : 1;
};

struct sun8i_codec {
struct regmap *regmap;
struct clk *clk_module;
const struct sun8i_codec_quirks *quirks;
struct sun8i_codec_aif aifs[NAIFS];
+ unsigned int sysclk_rate;
+ int sysclk_refcnt;
};

static int sun8i_codec_runtime_resume(struct device *dev)
{
struct sun8i_codec *scodec = dev_get_drvdata(dev);
int ret;

regcache_cache_only(scodec->regmap, false);
@@ -319,16 +321,57 @@ static int sun8i_codec_set_tdm_slot(struct snd_soc_dai *dai,
return -EINVAL;

aif->slots = slots;
aif->slot_width = slot_width;

return 0;
}

+static const unsigned int sun8i_codec_rates[] = {
+ 7350, 8000, 11025, 12000, 14700, 16000, 22050, 24000,
+ 29400, 32000, 44100, 48000, 88200, 96000, 176400, 192000,
+};
+
+static const struct snd_pcm_hw_constraint_list sun8i_codec_all_rates = {
+ .list = sun8i_codec_rates,
+ .count = ARRAY_SIZE(sun8i_codec_rates),
+};
+
+static const struct snd_pcm_hw_constraint_list sun8i_codec_22M_rates = {
+ .list = sun8i_codec_rates,
+ .count = ARRAY_SIZE(sun8i_codec_rates),
+ .mask = 0x5555,
+};
+
+static const struct snd_pcm_hw_constraint_list sun8i_codec_24M_rates = {
+ .list = sun8i_codec_rates,
+ .count = ARRAY_SIZE(sun8i_codec_rates),
+ .mask = 0xaaaa,
+};
+
+static int sun8i_codec_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
+ const struct snd_pcm_hw_constraint_list *list;
+
+ if (!scodec->sysclk_refcnt)
+ list = &sun8i_codec_all_rates;
+ else if (scodec->sysclk_rate == 22579200)
+ list = &sun8i_codec_22M_rates;
+ else if (scodec->sysclk_rate == 24576000)
+ list = &sun8i_codec_24M_rates;
+ else
+ return -EINVAL;
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, list);
+}
+
struct sun8i_codec_clk_div {
u8 div;
u8 val;
};

static const struct sun8i_codec_clk_div sun8i_codec_bclk_div[] = {
{ .div = 1, .val = 0 },
{ .div = 2, .val = 1 },
@@ -341,22 +384,21 @@ static const struct sun8i_codec_clk_div sun8i_codec_bclk_div[] = {
{ .div = 32, .val = 8 },
{ .div = 48, .val = 9 },
{ .div = 64, .val = 10 },
{ .div = 96, .val = 11 },
{ .div = 128, .val = 12 },
{ .div = 192, .val = 13 },
};

-static u8 sun8i_codec_get_bclk_div(struct sun8i_codec *scodec,
+static u8 sun8i_codec_get_bclk_div(unsigned int sysclk_rate,
unsigned int lrck_div_order,
unsigned int sample_rate)
{
- unsigned long clk_rate = clk_get_rate(scodec->clk_module);
- unsigned int div = clk_rate / sample_rate >> lrck_div_order;
+ unsigned int div = sysclk_rate / sample_rate >> lrck_div_order;
unsigned int best_val = 0, best_diff = ~0;
int i;

for (i = 0; i < ARRAY_SIZE(sun8i_codec_bclk_div); i++) {
const struct sun8i_codec_clk_div *bdiv = &sun8i_codec_bclk_div[i];
unsigned int diff = abs(bdiv->div - div);

if (diff < best_diff) {
@@ -383,16 +425,17 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
struct sun8i_codec_aif *aif = &scodec->aifs[dai->id];
unsigned int sample_rate = params_rate(params);
unsigned int slots = aif->slots ?: params_channels(params);
unsigned int slot_width = aif->slot_width ?: params_width(params);
+ unsigned int sysclk_rate = clk_get_rate(scodec->clk_module);
int lrck_div_order, word_size;
u8 bclk_div;

/* word size */
switch (params_width(params)) {
case 8:
word_size = 0x0;
break;
@@ -418,46 +461,54 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
if (lrck_div_order < 0)
return lrck_div_order;

regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK,
(lrck_div_order - 4) << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV);

/* BCLK divider (SYSCLK/BCLK ratio) */
- bclk_div = sun8i_codec_get_bclk_div(scodec, lrck_div_order, sample_rate);
+ bclk_div = sun8i_codec_get_bclk_div(sysclk_rate, lrck_div_order, sample_rate);
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK,
bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV);

+ if (!aif->open_streams) {
+ scodec->sysclk_rate = sysclk_rate;
+ scodec->sysclk_refcnt++;
+ }
+
aif->sample_rate = sample_rate;
aif->open_streams |= BIT(substream->stream);

return sun8i_codec_update_sample_rate(scodec);
}

static int sun8i_codec_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
struct sun8i_codec_aif *aif = &scodec->aifs[dai->id];

if (aif->open_streams != BIT(substream->stream))
goto done;

+ scodec->sysclk_refcnt--;
+
aif->sample_rate = 0;

done:
aif->open_streams &= ~BIT(substream->stream);
return 0;
}

static const struct snd_soc_dai_ops sun8i_codec_dai_ops = {
.set_fmt = sun8i_codec_set_fmt,
.set_tdm_slot = sun8i_codec_set_tdm_slot,
+ .startup = sun8i_codec_startup,
.hw_params = sun8i_codec_hw_params,
.hw_free = sun8i_codec_hw_free,
};

static struct snd_soc_dai_driver sun8i_codec_dais[] = {
{
.name = "sun8i-codec-aif1",
.id = AIF1,
--
2.26.2

2020-10-01 02:16:48

by Samuel Holland

[permalink] [raw]
Subject: [PATCH 20/25] ASoC: sun8i-codec: Protect the clock rate while streams are open

The codec's clock input is shared among all AIFs, and shared with other
audio-related hardware in the SoC, including I2S and SPDIF controllers.
To ensure sample rates selected by userspace or by codec2codec DAI links
are maintained, the clock rate must be protected while it is in use.

Signed-off-by: Samuel Holland <[email protected]>
---
sound/soc/sunxi/sun8i-codec.c | 25 ++++++++++++++++++++++---
1 file changed, 22 insertions(+), 3 deletions(-)

diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
index 501af64d43a0..86065bee7cd3 100644
--- a/sound/soc/sunxi/sun8i-codec.c
+++ b/sound/soc/sunxi/sun8i-codec.c
@@ -416,27 +416,32 @@ static int sun8i_codec_get_lrck_div_order(unsigned int slots,
unsigned int div = slots * slot_width;

if (div < 16 || div > 256)
return -EINVAL;

return order_base_2(div);
}

+static unsigned int sun8i_codec_get_sysclk_rate(unsigned int sample_rate)
+{
+ return sample_rate % 4000 ? 22579200 : 24576000;
+}
+
static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
struct sun8i_codec_aif *aif = &scodec->aifs[dai->id];
unsigned int sample_rate = params_rate(params);
unsigned int slots = aif->slots ?: params_channels(params);
unsigned int slot_width = aif->slot_width ?: params_width(params);
- unsigned int sysclk_rate = clk_get_rate(scodec->clk_module);
- int lrck_div_order, word_size;
+ unsigned int sysclk_rate = sun8i_codec_get_sysclk_rate(sample_rate);
+ int lrck_div_order, ret, word_size;
u8 bclk_div;

/* word size */
switch (params_width(params)) {
case 8:
word_size = 0x0;
break;
case 16:
@@ -466,17 +471,30 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
(lrck_div_order - 4) << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV);

/* BCLK divider (SYSCLK/BCLK ratio) */
bclk_div = sun8i_codec_get_bclk_div(sysclk_rate, lrck_div_order, sample_rate);
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK,
bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV);

- if (!aif->open_streams) {
+ /* SYSCLK rate */
+ if (aif->open_streams) {
+ ret = clk_set_rate(scodec->clk_module, sysclk_rate);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = clk_set_rate_exclusive(scodec->clk_module, sysclk_rate);
+ if (ret == -EBUSY)
+ dev_err(dai->dev, "%s: clock is busy! Sample rate %u Hz "
+ "conflicts with other audio streams.\n",
+ dai->name, sample_rate);
+ if (ret < 0)
+ return ret;
+
scodec->sysclk_rate = sysclk_rate;
scodec->sysclk_refcnt++;
}

aif->sample_rate = sample_rate;
aif->open_streams |= BIT(substream->stream);

return sun8i_codec_update_sample_rate(scodec);
@@ -487,16 +505,17 @@ static int sun8i_codec_hw_free(struct snd_pcm_substream *substream,
{
struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
struct sun8i_codec_aif *aif = &scodec->aifs[dai->id];

if (aif->open_streams != BIT(substream->stream))
goto done;

scodec->sysclk_refcnt--;
+ clk_rate_exclusive_put(scodec->clk_module);

aif->sample_rate = 0;

done:
aif->open_streams &= ~BIT(substream->stream);
return 0;
}

--
2.26.2

2020-10-01 02:17:14

by Samuel Holland

[permalink] [raw]
Subject: [PATCH 13/25] ASoC: sun8i-codec: Round up the LRCK divisor

The codec supports only power-of-two BCLK/LRCK divisors. If either the
slot width or the number of slots is not a power of two, the LRCK
divisor must be rounded up to provide enough space. To do that, use
order_base_2 (instead of ilog2, which rounds down).

Since the rounded divisor is also needed for setting the SYSCLK/BCLK
divisor, return the order base 2 instead of fully calculating the
hardware register encoding.

Signed-off-by: Samuel Holland <[email protected]>
---
sound/soc/sunxi/sun8i-codec.c | 22 ++++++++++++----------
1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
index e7f01a4b4001..779853d023fe 100644
--- a/sound/soc/sunxi/sun8i-codec.c
+++ b/sound/soc/sunxi/sun8i-codec.c
@@ -300,33 +300,35 @@ static u8 sun8i_codec_get_bclk_div(struct sun8i_codec *scodec,
best_diff = diff;
best_val = bdiv->val;
}
}

return best_val;
}

-static int sun8i_codec_get_lrck_div(unsigned int channels,
- unsigned int word_size)
+static int sun8i_codec_get_lrck_div_order(unsigned int slots,
+ unsigned int slot_width)
{
- unsigned int div = word_size * channels;
+ unsigned int div = slots * slot_width;

if (div < 16 || div > 256)
return -EINVAL;

- return ilog2(div) - 4;
+ return order_base_2(div);
}

static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
- int lrck_div, sample_rate, word_size;
+ unsigned int slots = params_channels(params);
+ unsigned int slot_width = params_width(params);
+ int lrck_div_order, sample_rate, word_size;
u8 bclk_div;

/* word size */
switch (params_width(params)) {
case 8:
word_size = 0x0;
break;
case 16:
@@ -346,24 +348,24 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK,
word_size << SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ);

bclk_div = sun8i_codec_get_bclk_div(scodec, params_rate(params), 16);
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK,
bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV);

- lrck_div = sun8i_codec_get_lrck_div(params_channels(params),
- params_physical_width(params));
- if (lrck_div < 0)
- return lrck_div;
+ /* LRCK divider (BCLK/LRCK ratio) */
+ lrck_div_order = sun8i_codec_get_lrck_div_order(slots, slot_width);
+ if (lrck_div_order < 0)
+ return lrck_div_order;

regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK,
- lrck_div << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV);
+ (lrck_div_order - 4) << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV);

sample_rate = sun8i_codec_get_hw_rate(params);
if (sample_rate < 0)
return sample_rate;

regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
SUN8I_SYS_SR_CTRL_AIF1_FS_MASK,
sample_rate << SUN8I_SYS_SR_CTRL_AIF1_FS);
--
2.26.2

2020-10-01 02:17:18

by Samuel Holland

[permalink] [raw]
Subject: [PATCH 03/25] ASoC: sun8i-codec: Sort DAPM controls, widgets, and routes

Sort the remaining pieces of the DAPM driver so that they are all in the
same order among controls/widgets/routes, and so they roughly match the
register word and bit order of the hardware. This nicely separates the
AIF-related widgets from the ADC/DAC widgets, which allows the AIF
widgets to stay in a logical order as more AIFs are added to the driver.

No widgets are renamed, to ease verification that this commit makes no
functional change.

Signed-off-by: Samuel Holland <[email protected]>
---
sound/soc/sunxi/sun8i-codec.c | 101 ++++++++++++++++++----------------
1 file changed, 53 insertions(+), 48 deletions(-)

diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
index 6887a2e897f4..d14243c434f9 100644
--- a/sound/soc/sunxi/sun8i-codec.c
+++ b/sound/soc/sunxi/sun8i-codec.c
@@ -327,16 +327,35 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,

regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
SUN8I_SYS_SR_CTRL_AIF1_FS_MASK,
sample_rate << SUN8I_SYS_SR_CTRL_AIF1_FS);

return 0;
}

+static const struct snd_kcontrol_new sun8i_aif1_ad0_mixer_controls[] = {
+ SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital ADC Capture Switch",
+ SUN8I_AIF1_MXR_SRC,
+ SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L,
+ SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R, 1, 0),
+ SOC_DAPM_DOUBLE("AIF2 Digital ADC Capture Switch",
+ SUN8I_AIF1_MXR_SRC,
+ SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL,
+ SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR, 1, 0),
+ SOC_DAPM_DOUBLE("AIF1 Data Digital ADC Capture Switch",
+ SUN8I_AIF1_MXR_SRC,
+ SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_ADCL,
+ SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR, 1, 0),
+ SOC_DAPM_DOUBLE("AIF2 Inv Digital ADC Capture Switch",
+ SUN8I_AIF1_MXR_SRC,
+ SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACR,
+ SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL, 1, 0),
+};
+
static const struct snd_kcontrol_new sun8i_dac_mixer_controls[] = {
SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital DAC Playback Switch",
SUN8I_DAC_MXR_SRC,
SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L,
SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R, 1, 0),
SOC_DAPM_DOUBLE("AIF1 Slot 1 Digital DAC Playback Switch",
SUN8I_DAC_MXR_SRC,
SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L,
@@ -344,34 +363,16 @@ static const struct snd_kcontrol_new sun8i_dac_mixer_controls[] = {
SOC_DAPM_DOUBLE("AIF2 Digital DAC Playback Switch", SUN8I_DAC_MXR_SRC,
SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL,
SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR, 1, 0),
SOC_DAPM_DOUBLE("ADC Digital DAC Playback Switch", SUN8I_DAC_MXR_SRC,
SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL,
SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR, 1, 0),
};

-static const struct snd_kcontrol_new sun8i_input_mixer_controls[] = {
- SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital ADC Capture Switch",
- SUN8I_AIF1_MXR_SRC,
- SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L,
- SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R, 1, 0),
- SOC_DAPM_DOUBLE("AIF2 Digital ADC Capture Switch", SUN8I_AIF1_MXR_SRC,
- SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL,
- SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR, 1, 0),
- SOC_DAPM_DOUBLE("AIF1 Data Digital ADC Capture Switch",
- SUN8I_AIF1_MXR_SRC,
- SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_ADCL,
- SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR, 1, 0),
- SOC_DAPM_DOUBLE("AIF2 Inv Digital ADC Capture Switch",
- SUN8I_AIF1_MXR_SRC,
- SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACR,
- SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL, 1, 0),
-};
-
static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
/* System Clocks */
SND_SOC_DAPM_CLOCK_SUPPLY("mod"),

SND_SOC_DAPM_SUPPLY("AIF1CLK",
SUN8I_SYSCLK_CTL,
SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("SYSCLK",
@@ -395,55 +396,59 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
SUN8I_MOD_RST_CTL_AIF1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("RST ADC",
SUN8I_MOD_RST_CTL,
SUN8I_MOD_RST_CTL_ADC, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("RST DAC",
SUN8I_MOD_RST_CTL,
SUN8I_MOD_RST_CTL_DAC, 0, NULL, 0),

- /* Digital parts of the DACs and ADC */
- SND_SOC_DAPM_SUPPLY("DAC", SUN8I_DAC_DIG_CTRL, SUN8I_DAC_DIG_CTRL_ENDA,
- 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("ADC", SUN8I_ADC_DIG_CTRL, SUN8I_ADC_DIG_CTRL_ENAD,
- 0, NULL, 0),
-
- /* AIF "DAC" Inputs */
- SND_SOC_DAPM_AIF_IN("AIF1 DA0L", "Playback", 0,
- SUN8I_AIF1_DACDAT_CTRL,
- SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0),
- SND_SOC_DAPM_AIF_IN("AIF1 DA0R", "Playback", 0,
- SUN8I_AIF1_DACDAT_CTRL,
- SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0),
+ /* Module Supplies */
+ SND_SOC_DAPM_SUPPLY("ADC",
+ SUN8I_ADC_DIG_CTRL,
+ SUN8I_ADC_DIG_CTRL_ENAD, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC",
+ SUN8I_DAC_DIG_CTRL,
+ SUN8I_DAC_DIG_CTRL_ENDA, 0, NULL, 0),

/* AIF "ADC" Outputs */
SND_SOC_DAPM_AIF_IN("AIF1 AD0L", "Capture", 0,
SUN8I_AIF1_ADCDAT_CTRL,
SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA, 0),
SND_SOC_DAPM_AIF_IN("AIF1 AD0R", "Capture", 0,
SUN8I_AIF1_ADCDAT_CTRL,
SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA, 0),

+ /* AIF "ADC" Mixers */
+ SOC_MIXER_ARRAY("Left Digital ADC Mixer", SND_SOC_NOPM, 0, 0,
+ sun8i_aif1_ad0_mixer_controls),
+ SOC_MIXER_ARRAY("Right Digital ADC Mixer", SND_SOC_NOPM, 0, 0,
+ sun8i_aif1_ad0_mixer_controls),
+
+ /* AIF "DAC" Inputs */
+ SND_SOC_DAPM_AIF_IN("AIF1 DA0L", "Playback", 0,
+ SUN8I_AIF1_DACDAT_CTRL,
+ SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0),
+ SND_SOC_DAPM_AIF_IN("AIF1 DA0R", "Playback", 0,
+ SUN8I_AIF1_DACDAT_CTRL,
+ SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0),
+
/* ADC Inputs (connected to analog codec DAPM context) */
SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),

/* DAC Outputs (connected to analog codec DAPM context) */
SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),

- /* DAC and ADC Mixers */
+ /* DAC Mixers */
SOC_MIXER_ARRAY("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
sun8i_dac_mixer_controls),
SOC_MIXER_ARRAY("Right Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
sun8i_dac_mixer_controls),
- SOC_MIXER_ARRAY("Left Digital ADC Mixer", SND_SOC_NOPM, 0, 0,
- sun8i_input_mixer_controls),
- SOC_MIXER_ARRAY("Right Digital ADC Mixer", SND_SOC_NOPM, 0, 0,
- sun8i_input_mixer_controls),
};

static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
/* Clock Routes */
{ "AIF1CLK", NULL, "mod" },

{ "SYSCLK", NULL, "AIF1CLK" },

@@ -462,37 +467,37 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
{ "ADCR", NULL, "ADC" },

{ "CLK DAC", NULL, "SYSCLK" },
{ "RST DAC", NULL, "CLK DAC" },
{ "DAC", NULL, "RST DAC" },
{ "DACL", NULL, "DAC" },
{ "DACR", NULL, "DAC" },

- /* DAC Routes */
+ /* AIF "ADC" Output Routes */
+ { "AIF1 AD0L", NULL, "Left Digital ADC Mixer" },
+ { "AIF1 AD0R", NULL, "Right Digital ADC Mixer" },
+
+ /* AIF "ADC" Mixer Routes */
+ { "Left Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L" },
+ { "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCL" },
+
+ { "Right Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0R" },
+ { "Right Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCR" },
+
+ /* DAC Output Routes */
{ "DACL", NULL, "Left Digital DAC Mixer" },
{ "DACR", NULL, "Right Digital DAC Mixer" },

/* DAC Mixer Routes */
{ "Left Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0L" },
{ "Left Digital DAC Mixer", "ADC Digital DAC Playback Switch", "ADCL" },

{ "Right Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0R" },
{ "Right Digital DAC Mixer", "ADC Digital DAC Playback Switch", "ADCR" },
-
- /* ADC Routes */
- { "AIF1 AD0L", NULL, "Left Digital ADC Mixer" },
- { "AIF1 AD0R", NULL, "Right Digital ADC Mixer" },
-
- /* ADC Mixer Routes */
- { "Left Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L" },
- { "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCL" },
-
- { "Right Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0R" },
- { "Right Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCR" },
};

static const struct snd_soc_dapm_widget sun8i_codec_legacy_widgets[] = {
/* Legacy ADC Inputs (connected to analog codec DAPM context) */
SND_SOC_DAPM_ADC("AIF1 Slot 0 Left ADC", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_ADC("AIF1 Slot 0 Right ADC", NULL, SND_SOC_NOPM, 0, 0),

/* Legacy DAC Outputs (connected to analog codec DAPM context) */
--
2.26.2

2020-10-01 02:17:21

by Samuel Holland

[permalink] [raw]
Subject: [PATCH 12/25] ASoC: sun8i-codec: Program the correct word size

The hardware supports 8 to 24-bit word sizes on all three of its DAIs,
only one of which is connected to the CPU DAI. Program the word size
based on the actual selected format, instead of relying on limitations
in another driver (which, incedentally, has patches pending to remove
that limitation).

Signed-off-by: Samuel Holland <[email protected]>
---
sound/soc/sunxi/sun8i-codec.c | 27 ++++++++++++++++++++-------
1 file changed, 20 insertions(+), 7 deletions(-)

diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
index 506420fb355c..e7f01a4b4001 100644
--- a/sound/soc/sunxi/sun8i-codec.c
+++ b/sound/soc/sunxi/sun8i-codec.c
@@ -43,17 +43,16 @@
#define SUN8I_SYS_SR_CTRL_AIF1_FS 12
#define SUN8I_SYS_SR_CTRL_AIF2_FS 8
#define SUN8I_AIF1CLK_CTRL 0x040
#define SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD 15
#define SUN8I_AIF1CLK_CTRL_AIF1_CLK_INV 13
#define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV 9
#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV 6
#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ 4
-#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16 (1 << 4)
#define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT 2
#define SUN8I_AIF1_ADCDAT_CTRL 0x044
#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA 15
#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA 14
#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_SRC 10
#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_SRC 8
#define SUN8I_AIF1_DACDAT_CTRL 0x048
#define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA 15
@@ -317,26 +316,40 @@ static int sun8i_codec_get_lrck_div(unsigned int channels,
return ilog2(div) - 4;
}

static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
- int sample_rate, lrck_div;
+ int lrck_div, sample_rate, word_size;
u8 bclk_div;

- /*
- * The CPU DAI handles only a sample of 16 bits. Configure the
- * codec to handle this type of sample resolution.
- */
+ /* word size */
+ switch (params_width(params)) {
+ case 8:
+ word_size = 0x0;
+ break;
+ case 16:
+ word_size = 0x1;
+ break;
+ case 20:
+ word_size = 0x2;
+ break;
+ case 24:
+ word_size = 0x3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK,
- SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16);
+ word_size << SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ);

bclk_div = sun8i_codec_get_bclk_div(scodec, params_rate(params), 16);
regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK,
bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV);

lrck_div = sun8i_codec_get_lrck_div(params_channels(params),
params_physical_width(params));
--
2.26.2

2020-10-01 02:17:32

by Samuel Holland

[permalink] [raw]
Subject: [PATCH 05/25] ASoC: sun8i-codec: Correct DAPM widget types

Whie the aif_in and aif_out widget types are handled exactly the same in
the core DAPM code, a future widget event hook will need the correct
widget type to derive the associated substream. Clean up the widget type
for that reason, and so these widgets will match newly-added widgets for
the other AIFs.

Signed-off-by: Samuel Holland <[email protected]>
---
sound/soc/sunxi/sun8i-codec.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
index 30fe27648254..d0028883950c 100644
--- a/sound/soc/sunxi/sun8i-codec.c
+++ b/sound/soc/sunxi/sun8i-codec.c
@@ -405,22 +405,22 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("ADC",
SUN8I_ADC_DIG_CTRL,
SUN8I_ADC_DIG_CTRL_ENAD, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("DAC",
SUN8I_DAC_DIG_CTRL,
SUN8I_DAC_DIG_CTRL_ENDA, 0, NULL, 0),

/* AIF "ADC" Outputs */
- SND_SOC_DAPM_AIF_IN("AIF1 AD0L", "Capture", 0,
- SUN8I_AIF1_ADCDAT_CTRL,
- SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA, 0),
- SND_SOC_DAPM_AIF_IN("AIF1 AD0R", "Capture", 0,
- SUN8I_AIF1_ADCDAT_CTRL,
- SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1 AD0L", "Capture", 0,
+ SUN8I_AIF1_ADCDAT_CTRL,
+ SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1 AD0R", "Capture", 0,
+ SUN8I_AIF1_ADCDAT_CTRL,
+ SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA, 0),

/* AIF "ADC" Mixers */
SOC_MIXER_ARRAY("AIF1 AD0L Mixer", SND_SOC_NOPM, 0, 0,
sun8i_aif1_ad0_mixer_controls),
SOC_MIXER_ARRAY("AIF1 AD0R Mixer", SND_SOC_NOPM, 0, 0,
sun8i_aif1_ad0_mixer_controls),

/* AIF "DAC" Inputs */
--
2.26.2

2020-10-01 02:17:37

by Samuel Holland

[permalink] [raw]
Subject: [PATCH 04/25] ASoC: sun8i-codec: Consistently name DAPM widgets and routes

This cleans up the mixer widget names. The AIF1 AD0 Mixer names were
previously wrong -- they do not control the digital side of the ADC. The
DAC mixer widgets were not wrong, but they were verbose and did not
match the naming scheme of the other widgets.

The mixer controls are not renamed because they are exposed to
userspace.

Signed-off-by: Samuel Holland <[email protected]>
---
sound/soc/sunxi/sun8i-codec.c | 32 ++++++++++++++++----------------
1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
index d14243c434f9..30fe27648254 100644
--- a/sound/soc/sunxi/sun8i-codec.c
+++ b/sound/soc/sunxi/sun8i-codec.c
@@ -413,19 +413,19 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
SND_SOC_DAPM_AIF_IN("AIF1 AD0L", "Capture", 0,
SUN8I_AIF1_ADCDAT_CTRL,
SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA, 0),
SND_SOC_DAPM_AIF_IN("AIF1 AD0R", "Capture", 0,
SUN8I_AIF1_ADCDAT_CTRL,
SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA, 0),

/* AIF "ADC" Mixers */
- SOC_MIXER_ARRAY("Left Digital ADC Mixer", SND_SOC_NOPM, 0, 0,
+ SOC_MIXER_ARRAY("AIF1 AD0L Mixer", SND_SOC_NOPM, 0, 0,
sun8i_aif1_ad0_mixer_controls),
- SOC_MIXER_ARRAY("Right Digital ADC Mixer", SND_SOC_NOPM, 0, 0,
+ SOC_MIXER_ARRAY("AIF1 AD0R Mixer", SND_SOC_NOPM, 0, 0,
sun8i_aif1_ad0_mixer_controls),

/* AIF "DAC" Inputs */
SND_SOC_DAPM_AIF_IN("AIF1 DA0L", "Playback", 0,
SUN8I_AIF1_DACDAT_CTRL,
SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0),
SND_SOC_DAPM_AIF_IN("AIF1 DA0R", "Playback", 0,
SUN8I_AIF1_DACDAT_CTRL,
@@ -435,19 +435,19 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),

/* DAC Outputs (connected to analog codec DAPM context) */
SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),

/* DAC Mixers */
- SOC_MIXER_ARRAY("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
+ SOC_MIXER_ARRAY("DACL Mixer", SND_SOC_NOPM, 0, 0,
sun8i_dac_mixer_controls),
- SOC_MIXER_ARRAY("Right Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
+ SOC_MIXER_ARRAY("DACR Mixer", SND_SOC_NOPM, 0, 0,
sun8i_dac_mixer_controls),
};

static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
/* Clock Routes */
{ "AIF1CLK", NULL, "mod" },

{ "SYSCLK", NULL, "AIF1CLK" },
@@ -468,36 +468,36 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {

{ "CLK DAC", NULL, "SYSCLK" },
{ "RST DAC", NULL, "CLK DAC" },
{ "DAC", NULL, "RST DAC" },
{ "DACL", NULL, "DAC" },
{ "DACR", NULL, "DAC" },

/* AIF "ADC" Output Routes */
- { "AIF1 AD0L", NULL, "Left Digital ADC Mixer" },
- { "AIF1 AD0R", NULL, "Right Digital ADC Mixer" },
+ { "AIF1 AD0L", NULL, "AIF1 AD0L Mixer" },
+ { "AIF1 AD0R", NULL, "AIF1 AD0R Mixer" },

/* AIF "ADC" Mixer Routes */
- { "Left Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L" },
- { "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCL" },
+ { "AIF1 AD0L Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L" },
+ { "AIF1 AD0L Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCL" },

- { "Right Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0R" },
- { "Right Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCR" },
+ { "AIF1 AD0R Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0R" },
+ { "AIF1 AD0R Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCR" },

/* DAC Output Routes */
- { "DACL", NULL, "Left Digital DAC Mixer" },
- { "DACR", NULL, "Right Digital DAC Mixer" },
+ { "DACL", NULL, "DACL Mixer" },
+ { "DACR", NULL, "DACR Mixer" },

/* DAC Mixer Routes */
- { "Left Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0L" },
- { "Left Digital DAC Mixer", "ADC Digital DAC Playback Switch", "ADCL" },
+ { "DACL Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0L" },
+ { "DACL Mixer", "ADC Digital DAC Playback Switch", "ADCL" },

- { "Right Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0R" },
- { "Right Digital DAC Mixer", "ADC Digital DAC Playback Switch", "ADCR" },
+ { "DACR Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0R" },
+ { "DACR Mixer", "ADC Digital DAC Playback Switch", "ADCR" },
};

static const struct snd_soc_dapm_widget sun8i_codec_legacy_widgets[] = {
/* Legacy ADC Inputs (connected to analog codec DAPM context) */
SND_SOC_DAPM_ADC("AIF1 Slot 0 Left ADC", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_ADC("AIF1 Slot 0 Right ADC", NULL, SND_SOC_NOPM, 0, 0),

/* Legacy DAC Outputs (connected to analog codec DAPM context) */
--
2.26.2

2020-10-01 13:29:31

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH 20/25] ASoC: sun8i-codec: Protect the clock rate while streams are open

Hi Samuel,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on asoc/for-next]
[also build test ERROR on next-20200930]
[cannot apply to sunxi/sunxi/for-next v5.9-rc7]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url: https://github.com/0day-ci/linux/commits/Samuel-Holland/ASoC-sun8i-codec-support-for-AIF2-and-AIF3/20201001-101451
base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
config: sh-allmodconfig (attached as .config)
compiler: sh4-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/6a43c578b796aed3cd013c065ef444fa82b24fce
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Samuel-Holland/ASoC-sun8i-codec-support-for-AIF2-and-AIF3/20201001-101451
git checkout 6a43c578b796aed3cd013c065ef444fa82b24fce
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=sh

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>

All errors (new ones prefixed by >>, old ones prefixed by <<):

>> ERROR: modpost: "clk_set_rate_exclusive" [sound/soc/sunxi/sun8i-codec.ko] undefined!
>> ERROR: modpost: "clk_rate_exclusive_put" [sound/soc/sunxi/sun8i-codec.ko] undefined!
ERROR: modpost: "of_clk_add_hw_provider" [sound/soc/qcom/qdsp6/q6afe-clocks.ko] undefined!
ERROR: modpost: "devm_clk_hw_register" [sound/soc/qcom/qdsp6/q6afe-clocks.ko] undefined!
ERROR: modpost: "__delay" [drivers/net/phy/mdio-cavium.ko] undefined!

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]


Attachments:
(No filename) (2.02 kB)
.config.gz (51.51 kB)
Download all attachments

2020-10-05 09:56:52

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 03/25] ASoC: sun8i-codec: Sort DAPM controls, widgets, and routes

On Wed, Sep 30, 2020 at 09:11:26PM -0500, Samuel Holland wrote:
> Sort the remaining pieces of the DAPM driver so that they are all in the
> same order among controls/widgets/routes, and so they roughly match the
> register word and bit order of the hardware. This nicely separates the
> AIF-related widgets from the ADC/DAC widgets, which allows the AIF
> widgets to stay in a logical order as more AIFs are added to the driver.
>
> No widgets are renamed, to ease verification that this commit makes no
> functional change.
>
> Signed-off-by: Samuel Holland <[email protected]>

Acked-by: Maxime Ripard <[email protected]>

Maxime


Attachments:
(No filename) (653.00 B)
signature.asc (235.00 B)
Download all attachments

2020-10-05 09:59:20

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 05/25] ASoC: sun8i-codec: Correct DAPM widget types

On Wed, Sep 30, 2020 at 09:11:28PM -0500, Samuel Holland wrote:
> Whie the aif_in and aif_out widget types are handled exactly the same in
> the core DAPM code, a future widget event hook will need the correct
> widget type to derive the associated substream. Clean up the widget type
> for that reason, and so these widgets will match newly-added widgets for
> the other AIFs.
>
> Signed-off-by: Samuel Holland <[email protected]>

Acked-by: Maxime Ripard <[email protected]>

Maxime


Attachments:
(No filename) (501.00 B)
signature.asc (235.00 B)
Download all attachments

2020-10-05 09:59:20

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 04/25] ASoC: sun8i-codec: Consistently name DAPM widgets and routes

On Wed, Sep 30, 2020 at 09:11:27PM -0500, Samuel Holland wrote:
> This cleans up the mixer widget names. The AIF1 AD0 Mixer names were
> previously wrong -- they do not control the digital side of the ADC. The
> DAC mixer widgets were not wrong, but they were verbose and did not
> match the naming scheme of the other widgets.
>
> The mixer controls are not renamed because they are exposed to
> userspace.
>
> Signed-off-by: Samuel Holland <[email protected]>

Acked-by: Maxime Ripard <[email protected]>

Maxime


Attachments:
(No filename) (534.00 B)
signature.asc (235.00 B)
Download all attachments

2020-10-05 10:04:19

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 06/25] ASoC: sun8i-codec: Fix AIF widget channel references

On Wed, Sep 30, 2020 at 09:11:29PM -0500, Samuel Holland wrote:
> Both the left and right side widgets referenced channel 0. This would
> unnecessarily power on the right side widget (and its associated path)
> when a mono stream was active.
>
> Signed-off-by: Samuel Holland <[email protected]>

Acked-by: Maxime Ripard <[email protected]>

Maxime


Attachments:
(No filename) (363.00 B)
signature.asc (235.00 B)
Download all attachments

2020-10-05 11:28:16

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 10/25] ASoC: sun8i-codec: Program format before clock inversion

On Wed, Sep 30, 2020 at 09:11:33PM -0500, Samuel Holland wrote:
> The LRCK inversion bit has a different meaning in DSP mode: it selects
> between DSP A and DSP B formats. To support this, we need to know if
> the selected format is a DSP format. One easy way to do this is to set
> the format field before the inversion fields.
>
> Signed-off-by: Samuel Holland <[email protected]>

Acked-by: Maxime Ripard <[email protected]>

Maxime


Attachments:
(No filename) (451.00 B)
signature.asc (235.00 B)
Download all attachments

2020-10-05 11:34:52

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 12/25] ASoC: sun8i-codec: Program the correct word size

On Wed, Sep 30, 2020 at 09:11:35PM -0500, Samuel Holland wrote:
> The hardware supports 8 to 24-bit word sizes on all three of its DAIs,
> only one of which is connected to the CPU DAI. Program the word size
> based on the actual selected format, instead of relying on limitations
> in another driver (which, incedentally, has patches pending to remove
> that limitation).
>
> Signed-off-by: Samuel Holland <[email protected]>

Acked-by: Maxime Ripard <[email protected]>

Maxime


Attachments:
(No filename) (496.00 B)
signature.asc (235.00 B)
Download all attachments

2020-10-05 11:35:33

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 13/25] ASoC: sun8i-codec: Round up the LRCK divisor

On Wed, Sep 30, 2020 at 09:11:36PM -0500, Samuel Holland wrote:
> The codec supports only power-of-two BCLK/LRCK divisors. If either the
> slot width or the number of slots is not a power of two, the LRCK
> divisor must be rounded up to provide enough space. To do that, use
> order_base_2 (instead of ilog2, which rounds down).
>
> Since the rounded divisor is also needed for setting the SYSCLK/BCLK
> divisor, return the order base 2 instead of fully calculating the
> hardware register encoding.
>
> Signed-off-by: Samuel Holland <[email protected]>

Acked-by: Maxime Ripard <[email protected]>

Maxime


Attachments:
(No filename) (627.00 B)
signature.asc (235.00 B)
Download all attachments

2020-10-05 11:41:50

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 17/25] ASoC: sun8i-codec: Enable all supported sample rates

On Wed, Sep 30, 2020 at 09:11:40PM -0500, Samuel Holland wrote:
> The system sample rate programmed into the hardware is really a clock
> divider from SYSCLK to the ADC and DAC. Since we support two SYSCLK
> frequencies, we can use all sample rates corresponding to one of those
> frequencies divided by any available divisor.
>
> This commit enables support for those sample rates. It also stops
> advertising support for a 64 kHz sample rate, which is not supported.
>
> Signed-off-by: Samuel Holland <[email protected]>

Acked-by: Maxime Ripard <[email protected]>

Maxime


Attachments:
(No filename) (595.00 B)
signature.asc (235.00 B)
Download all attachments

2020-10-05 12:01:35

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 19/25] ASoC: sun8i-codec: Constrain to compatible sample rates

On Wed, Sep 30, 2020 at 09:11:42PM -0500, Samuel Holland wrote:
> While another stream is active, only allow userspace to use sample rates
> that are compatible with the current SYSCLK frequency. This ensures the
> actual sample rate will always match what is given in hw_params.
>
> Signed-off-by: Samuel Holland <[email protected]>

Acked-by: Maxime Ripard <[email protected]>

Maxime


Attachments:
(No filename) (401.00 B)
signature.asc (235.00 B)
Download all attachments

2020-10-05 12:03:17

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 20/25] ASoC: sun8i-codec: Protect the clock rate while streams are open

On Wed, Sep 30, 2020 at 09:11:43PM -0500, Samuel Holland wrote:
> The codec's clock input is shared among all AIFs, and shared with other
> audio-related hardware in the SoC, including I2S and SPDIF controllers.
> To ensure sample rates selected by userspace or by codec2codec DAI links
> are maintained, the clock rate must be protected while it is in use.
>
> Signed-off-by: Samuel Holland <[email protected]>
> ---
> sound/soc/sunxi/sun8i-codec.c | 25 ++++++++++++++++++++++---
> 1 file changed, 22 insertions(+), 3 deletions(-)
>
> diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
> index 501af64d43a0..86065bee7cd3 100644
> --- a/sound/soc/sunxi/sun8i-codec.c
> +++ b/sound/soc/sunxi/sun8i-codec.c
> @@ -416,27 +416,32 @@ static int sun8i_codec_get_lrck_div_order(unsigned int slots,
> unsigned int div = slots * slot_width;
>
> if (div < 16 || div > 256)
> return -EINVAL;
>
> return order_base_2(div);
> }
>
> +static unsigned int sun8i_codec_get_sysclk_rate(unsigned int sample_rate)
> +{
> + return sample_rate % 4000 ? 22579200 : 24576000;
> +}
> +
> static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
> struct snd_pcm_hw_params *params,
> struct snd_soc_dai *dai)
> {
> struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
> struct sun8i_codec_aif *aif = &scodec->aifs[dai->id];
> unsigned int sample_rate = params_rate(params);
> unsigned int slots = aif->slots ?: params_channels(params);
> unsigned int slot_width = aif->slot_width ?: params_width(params);
> - unsigned int sysclk_rate = clk_get_rate(scodec->clk_module);
> - int lrck_div_order, word_size;
> + unsigned int sysclk_rate = sun8i_codec_get_sysclk_rate(sample_rate);
> + int lrck_div_order, ret, word_size;
> u8 bclk_div;
>
> /* word size */
> switch (params_width(params)) {
> case 8:
> word_size = 0x0;
> break;
> case 16:
> @@ -466,17 +471,30 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
> (lrck_div_order - 4) << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV);
>
> /* BCLK divider (SYSCLK/BCLK ratio) */
> bclk_div = sun8i_codec_get_bclk_div(sysclk_rate, lrck_div_order, sample_rate);
> regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
> SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK,
> bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV);
>
> - if (!aif->open_streams) {
> + /* SYSCLK rate */
> + if (aif->open_streams) {
> + ret = clk_set_rate(scodec->clk_module, sysclk_rate);
> + if (ret < 0)
> + return ret;
> + } else {
> + ret = clk_set_rate_exclusive(scodec->clk_module, sysclk_rate);

It's not really clear to me why we wouldn't want to always protect the
clock rate here?

> + if (ret == -EBUSY)
> + dev_err(dai->dev, "%s: clock is busy! Sample rate %u Hz "
> + "conflicts with other audio streams.\n",

This string creates a checkpatch warning.

Maxime


Attachments:
(No filename) (2.92 kB)
signature.asc (235.00 B)
Download all attachments

2020-10-05 13:16:47

by Chen-Yu Tsai

[permalink] [raw]
Subject: Re: [PATCH 20/25] ASoC: sun8i-codec: Protect the clock rate while streams are open

On Mon, Oct 5, 2020 at 8:01 PM Maxime Ripard <[email protected]> wrote:
>
> On Wed, Sep 30, 2020 at 09:11:43PM -0500, Samuel Holland wrote:
> > The codec's clock input is shared among all AIFs, and shared with other
> > audio-related hardware in the SoC, including I2S and SPDIF controllers.
> > To ensure sample rates selected by userspace or by codec2codec DAI links
> > are maintained, the clock rate must be protected while it is in use.
> >
> > Signed-off-by: Samuel Holland <[email protected]>
> > ---
> > sound/soc/sunxi/sun8i-codec.c | 25 ++++++++++++++++++++++---
> > 1 file changed, 22 insertions(+), 3 deletions(-)
> >
> > diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
> > index 501af64d43a0..86065bee7cd3 100644
> > --- a/sound/soc/sunxi/sun8i-codec.c
> > +++ b/sound/soc/sunxi/sun8i-codec.c
> > @@ -416,27 +416,32 @@ static int sun8i_codec_get_lrck_div_order(unsigned int slots,
> > unsigned int div = slots * slot_width;
> >
> > if (div < 16 || div > 256)
> > return -EINVAL;
> >
> > return order_base_2(div);
> > }
> >
> > +static unsigned int sun8i_codec_get_sysclk_rate(unsigned int sample_rate)
> > +{
> > + return sample_rate % 4000 ? 22579200 : 24576000;
> > +}
> > +
> > static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
> > struct snd_pcm_hw_params *params,
> > struct snd_soc_dai *dai)
> > {
> > struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
> > struct sun8i_codec_aif *aif = &scodec->aifs[dai->id];
> > unsigned int sample_rate = params_rate(params);
> > unsigned int slots = aif->slots ?: params_channels(params);
> > unsigned int slot_width = aif->slot_width ?: params_width(params);
> > - unsigned int sysclk_rate = clk_get_rate(scodec->clk_module);
> > - int lrck_div_order, word_size;
> > + unsigned int sysclk_rate = sun8i_codec_get_sysclk_rate(sample_rate);
> > + int lrck_div_order, ret, word_size;
> > u8 bclk_div;
> >
> > /* word size */
> > switch (params_width(params)) {
> > case 8:
> > word_size = 0x0;
> > break;
> > case 16:
> > @@ -466,17 +471,30 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
> > (lrck_div_order - 4) << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV);
> >
> > /* BCLK divider (SYSCLK/BCLK ratio) */
> > bclk_div = sun8i_codec_get_bclk_div(sysclk_rate, lrck_div_order, sample_rate);
> > regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
> > SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK,
> > bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV);
> >
> > - if (!aif->open_streams) {
> > + /* SYSCLK rate */
> > + if (aif->open_streams) {
> > + ret = clk_set_rate(scodec->clk_module, sysclk_rate);
> > + if (ret < 0)
> > + return ret;
> > + } else {
> > + ret = clk_set_rate_exclusive(scodec->clk_module, sysclk_rate);
>
> It's not really clear to me why we wouldn't want to always protect the
> clock rate here?

I believe the intention is to allow a window, i.e. when no audio
blocks are running,
when it is possible to switch between sample rate families?

ChenYu

> > + if (ret == -EBUSY)
> > + dev_err(dai->dev, "%s: clock is busy! Sample rate %u Hz "
> > + "conflicts with other audio streams.\n",
>
> This string creates a checkpatch warning.
>
> Maxime

2020-10-05 18:19:48

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH 00/25] ASoC: sun8i-codec: support for AIF2 and AIF3

On Wed, 30 Sep 2020 21:11:23 -0500, Samuel Holland wrote:
> This series adds support the other two AIFs present in the sun8i codec,
> which can be used for codec2codec DAI links.
>
> This series first cleans up the DAPM component driver so there is an
> organized place to put the new widgets. Then it fills out the DAI
> driver, removing assumptions that were made for AIF1 (16 bits, 2
> channels, certain clock inversions). Some new logic is required to
> handle 3 DAIs and the ADC/DAC sharing the same clock. Finally, it adds
> the new DAIs, and hooks them up with DAPM widgets and routes per the
> hardware topology.
>
> [...]

Applied to

https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next

Thanks!

[1/8] ASoC: sun8i-codec: Set up clock tree at probe time
commit: d8f006825ac57e34f7ed5e63a1e16d889dc1508e
[2/8] ASoC: sun8i-codec: Swap module clock/reset dependencies
commit: ed3caa3bd44c9ae1cebeb32d787adc5ed35e29fa
[3/8] ASoC: sun8i-codec: Sort DAPM controls, widgets, and routes
commit: d58b7247087900414aa3e988e70ecba85e06f412
[4/8] ASoC: sun8i-codec: Consistently name DAPM widgets and routes
commit: 7b51f3c7029fab706e7d9ac99f67cbcf8f29beca
[5/8] ASoC: sun8i-codec: Correct DAPM widget types
commit: fc5668f62d089ba69b343f0e80146f5a3bc6fa71
[6/8] ASoC: sun8i-codec: Fix AIF widget channel references
commit: 4ab60cef3149d57fe56add8c60ee7e6d45816f27
[7/8] ASoC: sun8i-codec: Enable AIF mono/stereo control
commit: 18ebd62c30f0380da11d6c86e20b56c771ac1e18
[8/8] ASoC: sun8i-codec: Use snd_soc_dai_get_drvdata
commit: a886990c9525e83146829c7711ce444ff652c98a

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

2020-10-06 04:45:24

by Samuel Holland

[permalink] [raw]
Subject: Re: [PATCH 20/25] ASoC: sun8i-codec: Protect the clock rate while streams are open

On 10/5/20 7:01 AM, Maxime Ripard wrote:
> On Wed, Sep 30, 2020 at 09:11:43PM -0500, Samuel Holland wrote:
>> The codec's clock input is shared among all AIFs, and shared with other
>> audio-related hardware in the SoC, including I2S and SPDIF controllers.
>> To ensure sample rates selected by userspace or by codec2codec DAI links
>> are maintained, the clock rate must be protected while it is in use.
>>
>> Signed-off-by: Samuel Holland <[email protected]>
>> ---
>> sound/soc/sunxi/sun8i-codec.c | 25 ++++++++++++++++++++++---
>> 1 file changed, 22 insertions(+), 3 deletions(-)
>>
>> diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
>> index 501af64d43a0..86065bee7cd3 100644
>> --- a/sound/soc/sunxi/sun8i-codec.c
>> +++ b/sound/soc/sunxi/sun8i-codec.c
>> @@ -416,27 +416,32 @@ static int sun8i_codec_get_lrck_div_order(unsigned int slots,
>> unsigned int div = slots * slot_width;
>>
>> if (div < 16 || div > 256)
>> return -EINVAL;
>>
>> return order_base_2(div);
>> }
>>
>> +static unsigned int sun8i_codec_get_sysclk_rate(unsigned int sample_rate)
>> +{
>> + return sample_rate % 4000 ? 22579200 : 24576000;
>> +}
>> +
>> static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
>> struct snd_pcm_hw_params *params,
>> struct snd_soc_dai *dai)
>> {
>> struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
>> struct sun8i_codec_aif *aif = &scodec->aifs[dai->id];
>> unsigned int sample_rate = params_rate(params);
>> unsigned int slots = aif->slots ?: params_channels(params);
>> unsigned int slot_width = aif->slot_width ?: params_width(params);
>> - unsigned int sysclk_rate = clk_get_rate(scodec->clk_module);
>> - int lrck_div_order, word_size;
>> + unsigned int sysclk_rate = sun8i_codec_get_sysclk_rate(sample_rate);
>> + int lrck_div_order, ret, word_size;
>> u8 bclk_div;
>>
>> /* word size */
>> switch (params_width(params)) {
>> case 8:
>> word_size = 0x0;
>> break;
>> case 16:
>> @@ -466,17 +471,30 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
>> (lrck_div_order - 4) << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV);
>>
>> /* BCLK divider (SYSCLK/BCLK ratio) */
>> bclk_div = sun8i_codec_get_bclk_div(sysclk_rate, lrck_div_order, sample_rate);
>> regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
>> SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK,
>> bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV);
>>
>> - if (!aif->open_streams) {
>> + /* SYSCLK rate */
>> + if (aif->open_streams) {
>> + ret = clk_set_rate(scodec->clk_module, sysclk_rate);
>> + if (ret < 0)
>> + return ret;
>> + } else {
>> + ret = clk_set_rate_exclusive(scodec->clk_module, sysclk_rate);
>
> It's not really clear to me why we wouldn't want to always protect the
> clock rate here?

From Documentation/sound/kernel-api/writing-an-alsa-driver.rst:

hw_params callback
...
Note that this and ``prepare`` callbacks may be called multiple
times per initialization. For example, the OSS emulation may call
these callbacks at each change via its ioctl.

Clock rate protection is reference counted, so we must only take one
reference (or at least a known number of references) per stream.

>> + if (ret == -EBUSY)
>> + dev_err(dai->dev, "%s: clock is busy! Sample rate %u Hz "
>> + "conflicts with other audio streams.\n",
>
> This string creates a checkpatch warning.

I will put it on one line, though >100 columns is also a checkpatch warning.

> Maxime

Cheers,
Samuel

2020-10-06 04:47:30

by Samuel Holland

[permalink] [raw]
Subject: Re: [PATCH 20/25] ASoC: sun8i-codec: Protect the clock rate while streams are open

On 10/5/20 8:15 AM, Chen-Yu Tsai wrote:
> On Mon, Oct 5, 2020 at 8:01 PM Maxime Ripard <[email protected]> wrote:
>>
>> On Wed, Sep 30, 2020 at 09:11:43PM -0500, Samuel Holland wrote:
>>> The codec's clock input is shared among all AIFs, and shared with other
>>> audio-related hardware in the SoC, including I2S and SPDIF controllers.
>>> To ensure sample rates selected by userspace or by codec2codec DAI links
>>> are maintained, the clock rate must be protected while it is in use.
>>>
>>> Signed-off-by: Samuel Holland <[email protected]>
>>> ---
>>> sound/soc/sunxi/sun8i-codec.c | 25 ++++++++++++++++++++++---
>>> 1 file changed, 22 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
>>> index 501af64d43a0..86065bee7cd3 100644
>>> --- a/sound/soc/sunxi/sun8i-codec.c
>>> +++ b/sound/soc/sunxi/sun8i-codec.c
...
>>> @@ -466,17 +471,30 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
>>> (lrck_div_order - 4) << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV);
>>>
>>> /* BCLK divider (SYSCLK/BCLK ratio) */
>>> bclk_div = sun8i_codec_get_bclk_div(sysclk_rate, lrck_div_order, sample_rate);
>>> regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
>>> SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK,
>>> bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV);
>>>
>>> - if (!aif->open_streams) {
>>> + /* SYSCLK rate */
>>> + if (aif->open_streams) {
>>> + ret = clk_set_rate(scodec->clk_module, sysclk_rate);
>>> + if (ret < 0)
>>> + return ret;
>>> + } else {
>>> + ret = clk_set_rate_exclusive(scodec->clk_module, sysclk_rate);
>>
>> It's not really clear to me why we wouldn't want to always protect the
>> clock rate here?
>
> I believe the intention is to allow a window, i.e. when no audio
> blocks are running,
> when it is possible to switch between sample rate families?

Yes, this is an advantage now. It would not be the case if sun4i-i2s did
something similar. It has the same problem that multiple separate sound
cards compete for one PLL.

> ChenYu

Cheers,
Samuel

2020-10-08 13:05:30

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 20/25] ASoC: sun8i-codec: Protect the clock rate while streams are open

On Mon, Oct 05, 2020 at 11:43:51PM -0500, Samuel Holland wrote:
> On 10/5/20 7:01 AM, Maxime Ripard wrote:
> > On Wed, Sep 30, 2020 at 09:11:43PM -0500, Samuel Holland wrote:
> >> The codec's clock input is shared among all AIFs, and shared with other
> >> audio-related hardware in the SoC, including I2S and SPDIF controllers.
> >> To ensure sample rates selected by userspace or by codec2codec DAI links
> >> are maintained, the clock rate must be protected while it is in use.
> >>
> >> Signed-off-by: Samuel Holland <[email protected]>
> >> ---
> >> sound/soc/sunxi/sun8i-codec.c | 25 ++++++++++++++++++++++---
> >> 1 file changed, 22 insertions(+), 3 deletions(-)
> >>
> >> diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
> >> index 501af64d43a0..86065bee7cd3 100644
> >> --- a/sound/soc/sunxi/sun8i-codec.c
> >> +++ b/sound/soc/sunxi/sun8i-codec.c
> >> @@ -416,27 +416,32 @@ static int sun8i_codec_get_lrck_div_order(unsigned int slots,
> >> unsigned int div = slots * slot_width;
> >>
> >> if (div < 16 || div > 256)
> >> return -EINVAL;
> >>
> >> return order_base_2(div);
> >> }
> >>
> >> +static unsigned int sun8i_codec_get_sysclk_rate(unsigned int sample_rate)
> >> +{
> >> + return sample_rate % 4000 ? 22579200 : 24576000;
> >> +}
> >> +
> >> static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
> >> struct snd_pcm_hw_params *params,
> >> struct snd_soc_dai *dai)
> >> {
> >> struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
> >> struct sun8i_codec_aif *aif = &scodec->aifs[dai->id];
> >> unsigned int sample_rate = params_rate(params);
> >> unsigned int slots = aif->slots ?: params_channels(params);
> >> unsigned int slot_width = aif->slot_width ?: params_width(params);
> >> - unsigned int sysclk_rate = clk_get_rate(scodec->clk_module);
> >> - int lrck_div_order, word_size;
> >> + unsigned int sysclk_rate = sun8i_codec_get_sysclk_rate(sample_rate);
> >> + int lrck_div_order, ret, word_size;
> >> u8 bclk_div;
> >>
> >> /* word size */
> >> switch (params_width(params)) {
> >> case 8:
> >> word_size = 0x0;
> >> break;
> >> case 16:
> >> @@ -466,17 +471,30 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
> >> (lrck_div_order - 4) << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV);
> >>
> >> /* BCLK divider (SYSCLK/BCLK ratio) */
> >> bclk_div = sun8i_codec_get_bclk_div(sysclk_rate, lrck_div_order, sample_rate);
> >> regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
> >> SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK,
> >> bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV);
> >>
> >> - if (!aif->open_streams) {
> >> + /* SYSCLK rate */
> >> + if (aif->open_streams) {
> >> + ret = clk_set_rate(scodec->clk_module, sysclk_rate);
> >> + if (ret < 0)
> >> + return ret;
> >> + } else {
> >> + ret = clk_set_rate_exclusive(scodec->clk_module, sysclk_rate);
> >
> > It's not really clear to me why we wouldn't want to always protect the
> > clock rate here?
>
> From Documentation/sound/kernel-api/writing-an-alsa-driver.rst:
>
> hw_params callback
> ...
> Note that this and ``prepare`` callbacks may be called multiple
> times per initialization. For example, the OSS emulation may call
> these callbacks at each change via its ioctl.
>
> Clock rate protection is reference counted, so we must only take one
> reference (or at least a known number of references) per stream.

Ah, right.

Can you add a comment to make that more obvious?

> >> + if (ret == -EBUSY)
> >> + dev_err(dai->dev, "%s: clock is busy! Sample rate %u Hz "
> >> + "conflicts with other audio streams.\n",
> >
> > This string creates a checkpatch warning.
>
> I will put it on one line, though >100 columns is also a checkpatch warning.

Yeah, but in general having an error on a single line is more important.
That way you can then grep for that error message

Maxime


Attachments:
(No filename) (3.96 kB)
signature.asc (235.00 B)
Download all attachments