Signed-off-by: Ryan Lee <[email protected]>
---
Changelog:
Added support for DSP_A and DSP_B format
Added 'max98927_dai_tdm_slot' function to set proper slot configuration.
Moved max98927->iface out of switch statement to avoid line duplication.
Added variable 'tdm_mode' to avoid BCLK overwrite after 'max98927_dai_tdm_slot' configure BCLK value.
sound/soc/codecs/max98927.c | 155 +++++++++++++++++++++++++++++++++++---------
sound/soc/codecs/max98927.h | 7 +-
2 files changed, 129 insertions(+), 33 deletions(-)
diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c
index d9dbbe7..a1d3935 100644
--- a/sound/soc/codecs/max98927.c
+++ b/sound/soc/codecs/max98927.c
@@ -1,7 +1,7 @@
/*
* max98927.c -- MAX98927 ALSA Soc Audio driver
*
- * Copyright (C) 2016 Maxim Integrated Products
+ * Copyright (C) 2016-2017 Maxim Integrated Products
* Author: Ryan Lee <[email protected]>
*
* This program is free software; you can redistribute it and/or modify it
@@ -146,6 +146,7 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
unsigned int mode = 0;
unsigned int format = 0;
+ bool use_pdm = false;
unsigned int invert = 0;
dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt);
@@ -187,22 +188,27 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- max98927->iface |= SND_SOC_DAIFMT_I2S;
format = MAX98927_PCM_FORMAT_I2S;
break;
case SND_SOC_DAIFMT_LEFT_J:
- max98927->iface |= SND_SOC_DAIFMT_LEFT_J;
format = MAX98927_PCM_FORMAT_LJ;
break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format = MAX98927_PCM_FORMAT_TDM_MODE1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = MAX98927_PCM_FORMAT_TDM_MODE0;
+ break;
case SND_SOC_DAIFMT_PDM:
- max98927->iface |= SND_SOC_DAIFMT_PDM;
+ use_pdm = true;
break;
default:
return -EINVAL;
}
+ max98927->iface = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
- /* pcm channel configuration */
- if (max98927->iface & (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J)) {
+ if (!use_pdm) {
+ /* pcm channel configuration */
regmap_update_bits(max98927->regmap,
MAX98927_R0018_PCM_RX_EN_A,
MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN,
@@ -217,13 +223,11 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
MAX98927_R003B_SPK_SRC_SEL,
MAX98927_SPK_SRC_MASK, 0);
- } else
regmap_update_bits(max98927->regmap,
- MAX98927_R0018_PCM_RX_EN_A,
- MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, 0);
-
- /* pdm channel configuration */
- if (max98927->iface & SND_SOC_DAIFMT_PDM) {
+ MAX98927_R0035_PDM_RX_CTRL,
+ MAX98927_PDM_RX_EN_MASK, 0);
+ } else {
+ /* pdm channel configuration */
regmap_update_bits(max98927->regmap,
MAX98927_R0035_PDM_RX_CTRL,
MAX98927_PDM_RX_EN_MASK, 1);
@@ -231,10 +235,11 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
regmap_update_bits(max98927->regmap,
MAX98927_R003B_SPK_SRC_SEL,
MAX98927_SPK_SRC_MASK, 3);
- } else
+
regmap_update_bits(max98927->regmap,
- MAX98927_R0035_PDM_RX_CTRL,
- MAX98927_PDM_RX_EN_MASK, 0);
+ MAX98927_R0018_PCM_RX_EN_A,
+ MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, 0);
+ }
return 0;
}
@@ -245,6 +250,21 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
13000000, 19200000,
};
+/* BCLKs per LRCLK */
+static const int bclk_sel_table[] = {
+ 32, 48, 64, 96, 128, 192, 256, 384, 512,
+};
+
+static int max98927_get_bclk_sel(int bclk)
+{
+ int i;
+ /* match BCLKs per LRCLK */
+ for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) {
+ if (bclk_sel_table[i] == bclk)
+ return i + 2;
+ }
+ return 0;
+}
static int max98927_set_clock(struct max98927_priv *max98927,
struct snd_pcm_hw_params *params)
{
@@ -270,23 +290,20 @@ static int max98927_set_clock(struct max98927_priv *max98927,
i << MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT);
}
- switch (blr_clk_ratio) {
- case 32:
- value = 2;
- break;
- case 48:
- value = 3;
- break;
- case 64:
- value = 4;
- break;
- default:
- return -EINVAL;
+ if (!max98927->tdm_mode) {
+ /* BCLK configuration */
+ value = max98927_get_bclk_sel(blr_clk_ratio);
+ if (!value) {
+ dev_err(codec->dev, "format unsupported %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0022_PCM_CLK_SETUP,
+ MAX98927_PCM_CLK_SETUP_BSEL_MASK,
+ value);
}
- regmap_update_bits(max98927->regmap,
- MAX98927_R0022_PCM_CLK_SETUP,
- MAX98927_PCM_CLK_SETUP_BSEL_MASK,
- value);
return 0;
}
@@ -386,6 +403,78 @@ static int max98927_dai_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
+static int max98927_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+ int bsel = 0;
+ unsigned int chan_sz = 0;
+
+ max98927->tdm_mode = true;
+
+ /* BCLK configuration */
+ bsel = max98927_get_bclk_sel(slots * slot_width);
+ if (bsel == 0) {
+ dev_err(codec->dev, "BCLK %d not supported\n",
+ slots * slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0022_PCM_CLK_SETUP,
+ MAX98927_PCM_CLK_SETUP_BSEL_MASK,
+ bsel);
+
+ /* Channel size configuration */
+ switch (slot_width) {
+ case 16:
+ chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(codec->dev, "format unsupported %d\n",
+ slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0020_PCM_MODE_CFG,
+ MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ /* Rx slot configuration */
+ regmap_write(max98927->regmap,
+ MAX98927_R0018_PCM_RX_EN_A,
+ rx_mask & 0xFF);
+ regmap_write(max98927->regmap,
+ MAX98927_R0019_PCM_RX_EN_B,
+ (rx_mask & 0xFF00) >> 8);
+
+ /* Tx slot configuration */
+ regmap_write(max98927->regmap,
+ MAX98927_R001A_PCM_TX_EN_A,
+ tx_mask & 0xFF);
+ regmap_write(max98927->regmap,
+ MAX98927_R001B_PCM_TX_EN_B,
+ (tx_mask & 0xFF00) >> 8);
+
+ /* Tx slot Hi-Z configuration */
+ regmap_write(max98927->regmap,
+ MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
+ ~tx_mask & 0xFF);
+ regmap_write(max98927->regmap,
+ MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
+ (~tx_mask & 0xFF00) >> 8);
+
+ return 0;
+}
+
#define MAX98927_RATES SNDRV_PCM_RATE_8000_48000
#define MAX98927_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
@@ -405,6 +494,7 @@ static int max98927_dai_set_sysclk(struct snd_soc_dai *dai,
.set_sysclk = max98927_dai_set_sysclk,
.set_fmt = max98927_dai_set_fmt,
.hw_params = max98927_dai_hw_params,
+ .set_tdm_slot = max98927_dai_tdm_slot,
};
static int max98927_dac_event(struct snd_soc_dapm_widget *w,
@@ -414,6 +504,9 @@ static int max98927_dac_event(struct snd_soc_dapm_widget *w,
struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ max98927->tdm_mode = 0;
+ break;
case SND_SOC_DAPM_POST_PMU:
regmap_update_bits(max98927->regmap,
MAX98927_R003A_AMP_EN,
diff --git a/sound/soc/codecs/max98927.h b/sound/soc/codecs/max98927.h
index ece6a60..9ea8397 100644
--- a/sound/soc/codecs/max98927.h
+++ b/sound/soc/codecs/max98927.h
@@ -1,7 +1,7 @@
/*
* max98927.h -- MAX98927 ALSA Soc Audio driver
*
- * Copyright 2013-15 Maxim Integrated Products
+ * Copyright (C) 2016-2017 Maxim Integrated Products
* Author: Ryan Lee <[email protected]>
*
* This program is free software; you can redistribute it and/or modify it
@@ -161,7 +161,9 @@
#define MAX98927_PCM_MODE_CFG_FORMAT_SHIFT (3)
#define MAX98927_PCM_FORMAT_I2S (0x0 << 0)
#define MAX98927_PCM_FORMAT_LJ (0x1 << 0)
-
+#define MAX98927_PCM_FORMAT_TDM_MODE0 (0x3 << 0)
+#define MAX98927_PCM_FORMAT_TDM_MODE1 (0x4 << 0)
+#define MAX98927_PCM_FORMAT_TDM_MODE2 (0x5 << 0)
#define MAX98927_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6)
#define MAX98927_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6)
#define MAX98927_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6)
@@ -268,5 +270,6 @@ struct max98927_priv {
unsigned int iface;
unsigned int master;
unsigned int digital_gain;
+ bool tdm_mode;
};
#endif
--
1.9.1
Signed-off-by: Ryan Lee <[email protected]>
---
Changelog:
Added a control("EnvTrack Headroom") to set different headroom value for envelop tracking function.
Added one more control for envelop tracking function enable/disable("EnvTrack Switch").
Removed writing process to register 0x0087 because it is read only register.
sound/soc/codecs/max98927.c | 19 +++++++++++++++----
sound/soc/codecs/max98927.h | 4 ++++
2 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c
index a1d3935..73c7a33 100644
--- a/sound/soc/codecs/max98927.c
+++ b/sound/soc/codecs/max98927.c
@@ -624,6 +624,18 @@ static SOC_ENUM_SINGLE_DECL(max98927_current_limit,
MAX98927_R0042_BOOST_CTRL1, 1,
max98927_current_limit_text);
+static const char * const max98927_env_track_headroom_text[] = {
+ "0.000V", "0.125V", "0.250V", "0.375V", "0.500V", "0.625V",
+ "0.750V", "0.875V", "1.000V", "1.125V", "1.250V", "1.375V",
+ "1.500V", "1.625V", "1.750V", "1.875V", "2.000V", "2.125V",
+ "2.250V", "2.375V", "2.500V", "2.625V", "2.750V", "2.875V",
+ "3.000V", "3.125V", "3.250V", "3.375V", "3.500V"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98927_env_track_headroom,
+ MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM, 0,
+ max98927_env_track_headroom_text);
+
static const struct snd_kcontrol_new max98927_snd_controls[] = {
SOC_SINGLE_TLV("Speaker Volume", MAX98927_R003C_SPK_GAIN,
0, 6, 0,
@@ -641,6 +653,9 @@ static SOC_ENUM_SINGLE_DECL(max98927_current_limit,
MAX98927_AMP_VOL_SEL_SHIFT, 1, 0),
SOC_ENUM("Boost Output Voltage", max98927_boost_voltage),
SOC_ENUM("Current Limit", max98927_current_limit),
+ SOC_SINGLE("EnvTrack Switch", MAX98927_R0086_ENV_TRACK_CTRL,
+ MAX98927_ENV_TRACKER_EN_SHIFT, 1, 0),
+ SOC_ENUM("EnvTrack Headroom", max98927_env_track_headroom),
};
static const struct snd_soc_dapm_route max98927_audio_map[] = {
@@ -744,10 +759,6 @@ static int max98927_probe(struct snd_soc_codec *codec)
regmap_write(max98927->regmap,
MAX98927_R0086_ENV_TRACK_CTRL,
0x01);
- regmap_write(max98927->regmap,
- MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ,
- 0x10);
-
/* voltage, current slot configuration */
regmap_write(max98927->regmap,
MAX98927_R001E_PCM_TX_CH_SRC_A,
diff --git a/sound/soc/codecs/max98927.h b/sound/soc/codecs/max98927.h
index 9ea8397..98b5f17 100644
--- a/sound/soc/codecs/max98927.h
+++ b/sound/soc/codecs/max98927.h
@@ -250,6 +250,10 @@
#define MAX98927_BROWNOUT_DSP_EN (0x1 << 2)
#define MAX98927_BROWNOUT_DSP_SHIFT (2)
+/* MAX98927_R0086_ENV_TRACK_CTRL */
+#define MAX98927_ENV_TRACKER_EN (0x1 << 0)
+#define MAX98927_ENV_TRACKER_EN_SHIFT (0)
+
/* MAX98927_R0100_SOFT_RESET */
#define MAX98927_SOFT_RESET (0x1 << 0)
--
1.9.1
On Mon, Sep 11, 2017 at 09:12:18AM -0700, Ryan Lee wrote:
> Signed-off-by: Ryan Lee <[email protected]>
> ---
Please make an effort to write changelogs that clearly describe the
change you're making. This is doing way more than just implementing DSP
mode, it's also adding a fairly complicated set_tdm_slot() implementation
which isn't mentioned at all. It probably needs splitting into multiple
patches as well.
> @@ -414,6 +504,9 @@ static int max98927_dac_event(struct snd_soc_dapm_widget *w,
> struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
>
> switch (event) {
> + case SND_SOC_DAPM_PRE_PMU:
> + max98927->tdm_mode = 0;
> + break;
> case SND_SOC_DAPM_POST_PMU:
> regmap_update_bits(max98927->regmap,
> MAX98927_R003A_AMP_EN,
Why is an event associated with the DAC changing something related to
the DAI format? These things should be unrelated.
>-----Original Message-----
>From: Mark Brown [mailto:[email protected]]
>Sent: Thursday, September 14, 2017 11:50 AM
>To: Ryan Lee <[email protected]>
>Cc: [email protected]; [email protected]; [email protected];
>[email protected]; [email protected]; linux-
>[email protected]; [email protected]
>Subject: Re: [PATCH 1/3] ASoC: max98927: Added support for DSP_A and
>DSP_B format
>
>On Mon, Sep 11, 2017 at 09:12:18AM -0700, Ryan Lee wrote:
>> Signed-off-by: Ryan Lee <[email protected]>
>> ---
>
>Please make an effort to write changelogs that clearly describe the change
>you're making. This is doing way more than just implementing DSP mode, it's
>also adding a fairly complicated set_tdm_slot() implementation which isn't
>mentioned at all. It probably needs splitting into multiple patches as well.
I split this patch as two separated patches and sent again.
One is about adding DSP_A, DSP_B support.
Another is about TDM slot configuration.
I added more changelog inside.
>
>> @@ -414,6 +504,9 @@ static int max98927_dac_event(struct
>snd_soc_dapm_widget *w,
>> struct max98927_priv *max98927 =
>snd_soc_codec_get_drvdata(codec);
>>
>> switch (event) {
>> + case SND_SOC_DAPM_PRE_PMU:
>> + max98927->tdm_mode = 0;
>> + break;
>> case SND_SOC_DAPM_POST_PMU:
>> regmap_update_bits(max98927->regmap,
>> MAX98927_R003A_AMP_EN,
>
>Why is an event associated with the DAC changing something related to the DAI
>format? These things should be unrelated.
In TDM mode, BCLK is being configured inside 'max98927_dai_tdm_slot' because TDM slot information(number of slots, slot width) is available in there.
In normal case, BCLK is being configured when 'hw_params' is called and TDM slot information is not available in this function.
So I added 'tdm_mode' variable to avoid overwrite BCLK configuration when 'hw_params' function is called after TDM slot configuration is done.
I wanted to clear this variable once playback is over so I added clear function when DAPM event is received.
I referred some codec driver change TDM related things when DAC event is received after TDM slot configuration is done.