[alsa-devel] [PATCH 1/3] ASoC: max98927: Added support for DSP_A and DSP_B format
Signed-off-by: Ryan Lee ryans.lee@maximintegrated.com --- 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 ryans.lee@maximintegrated.com * * 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 ryans.lee@maximintegrated.com * * 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
Signed-off-by: Ryan Lee ryans.lee@maximintegrated.com --- 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)
On Mon, Sep 11, 2017 at 09:12:18AM -0700, Ryan Lee wrote:
Signed-off-by: Ryan Lee ryans.lee@maximintegrated.com
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;
case SND_SOC_DAPM_POST_PMU: regmap_update_bits(max98927->regmap, MAX98927_R003A_AMP_EN,break;
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:broonie@kernel.org] Sent: Thursday, September 14, 2017 11:50 AM To: Ryan Lee RyanS.Lee@maximintegrated.com Cc: lgirdwood@gmail.com; perex@perex.cz; tiwai@suse.com; kuninori.morimoto.gx@renesas.com; alsa-devel@alsa-project.org; linux- kernel@vger.kernel.org; ryan.lee.maxim@gmail.com 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 ryans.lee@maximintegrated.com
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;
case SND_SOC_DAPM_POST_PMU: regmap_update_bits(max98927->regmap, MAX98927_R003A_AMP_EN,break;
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.
participants (2)
-
Mark Brown
-
Ryan Lee