[PATCH v2] ASoC: ak4458: Add DSD support for ak4458 and ak4497
ak4458 can't support DSD512 format, but ak4497 can, so add a new variable (dsd512) in ak4458_drvdata to distinguish these two platforms.
Add a new kcontrol for ak4497 codec for ak4497 has a specific pin selection.
In hw_params(), calculate bit clock according to different DSD format and configure DSD registers.
Signed-off-by: Shengjiu Wang shengjiu.wang@nxp.com --- changes in v2 - add more commit description.
sound/soc/codecs/ak4458.c | 82 +++++++++++++++++++++++++++++++++++---- sound/soc/codecs/ak4458.h | 5 ++- 2 files changed, 79 insertions(+), 8 deletions(-)
diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c index 763e6839428f..2ef8b591eb79 100644 --- a/sound/soc/codecs/ak4458.c +++ b/sound/soc/codecs/ak4458.c @@ -31,11 +31,13 @@ static const char *ak4458_supply_names[AK4458_NUM_SUPPLIES] = { struct ak4458_drvdata { struct snd_soc_dai_driver *dai_drv; const struct snd_soc_component_driver *comp_drv; + bool dsd512; /* DSD512 is supported or not */ };
/* AK4458 Codec Private Data */ struct ak4458_priv { struct regulator_bulk_data supplies[AK4458_NUM_SUPPLIES]; + const struct ak4458_drvdata *drvdata; struct device *dev; struct regmap *regmap; struct gpio_desc *reset_gpiod; @@ -136,6 +138,10 @@ static const char * const ak4458_ats_select_texts[] = { /* DIF2 bit Audio Interface Format Setting(BICK fs) */ static const char * const ak4458_dif_select_texts[] = {"32fs,48fs", "64fs",};
+/* DSD input pin select */ +static const char * const ak4497_dsd_input_pin_select[] = { + "16_17_19pin", "3_4_5pin"}; + static const struct soc_enum ak4458_dac1_dem_enum = SOC_ENUM_SINGLE(AK4458_01_CONTROL2, 1, ARRAY_SIZE(ak4458_dem_select_texts), @@ -175,6 +181,10 @@ static const struct soc_enum ak4458_dif_enum = SOC_ENUM_SINGLE(AK4458_00_CONTROL1, 3, ARRAY_SIZE(ak4458_dif_select_texts), ak4458_dif_select_texts); +static const struct soc_enum ak4497_dsdp_enum = + SOC_ENUM_SINGLE(AK4458_09_DSD2, 2, + ARRAY_SIZE(ak4497_dsd_input_pin_select), + ak4497_dsd_input_pin_select);
static int get_digfil(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -282,6 +292,7 @@ static const struct snd_kcontrol_new ak4497_snd_controls[] = { SOC_ENUM("AK4497 Sound Mode", ak4458_sm_enum), SOC_ENUM("AK4497 Attenuation transition Time Setting", ak4458_ats_enum), + SOC_ENUM("AK4497 DSD Data Input Pin", ak4497_dsdp_enum), };
/* ak4497 dapm widgets */ @@ -325,12 +336,54 @@ static int ak4458_hw_params(struct snd_pcm_substream *substream, struct snd_soc_component *component = dai->component; struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component); int pcm_width = max(params_physical_width(params), ak4458->slot_width); - int nfs1; - u8 format; + u8 format, dsdsel0, dsdsel1; + int nfs1, dsd_bclk;
nfs1 = params_rate(params); ak4458->fs = nfs1;
+ /* calculate bit clock */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_DSD_U8: + case SNDRV_PCM_FORMAT_DSD_U16_LE: + case SNDRV_PCM_FORMAT_DSD_U16_BE: + case SNDRV_PCM_FORMAT_DSD_U32_LE: + case SNDRV_PCM_FORMAT_DSD_U32_BE: + dsd_bclk = nfs1 * params_physical_width(params); + switch (dsd_bclk) { + case 2822400: + dsdsel0 = 0; + dsdsel1 = 0; + break; + case 5644800: + dsdsel0 = 1; + dsdsel1 = 0; + break; + case 11289600: + dsdsel0 = 0; + dsdsel1 = 1; + break; + case 22579200: + if (ak4458->drvdata->dsd512) { + dsdsel0 = 1; + dsdsel1 = 1; + } else { + dev_err(dai->dev, "DSD512 not supported.\n"); + return -EINVAL; + } + break; + default: + dev_err(dai->dev, "Unsupported dsd bclk.\n"); + return -EINVAL; + } + + snd_soc_component_update_bits(component, AK4458_06_DSD1, + AK4458_DSDSEL_MASK, dsdsel0); + snd_soc_component_update_bits(component, AK4458_09_DSD2, + AK4458_DSDSEL_MASK, dsdsel1); + break; + } + /* Master Clock Frequency Auto Setting Mode Enable */ snd_soc_component_update_bits(component, AK4458_00_CONTROL1, 0x80, 0x80);
@@ -355,6 +408,9 @@ static int ak4458_hw_params(struct snd_pcm_substream *substream, case SND_SOC_DAIFMT_DSP_B: format = AK4458_DIF_32BIT_MSB; break; + case SND_SOC_DAIFMT_PDM: + format = AK4458_DIF_32BIT_MSB; + break; default: return -EINVAL; } @@ -393,6 +449,7 @@ static int ak4458_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) case SND_SOC_DAIFMT_LEFT_J: case SND_SOC_DAIFMT_RIGHT_J: case SND_SOC_DAIFMT_DSP_B: + case SND_SOC_DAIFMT_PDM: ak4458->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; break; default: @@ -401,6 +458,12 @@ static int ak4458_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; }
+ /* DSD mode */ + snd_soc_component_update_bits(component, AK4458_02_CONTROL3, + AK4458_DP_MASK, + ak4458->fmt == SND_SOC_DAIFMT_PDM ? + AK4458_DP_MASK : 0); + ak4458_rstn_control(component, 0); ak4458_rstn_control(component, 1);
@@ -472,7 +535,10 @@ static int ak4458_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
#define AK4458_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S24_LE |\ - SNDRV_PCM_FMTBIT_S32_LE) + SNDRV_PCM_FMTBIT_S32_LE |\ + SNDRV_PCM_FMTBIT_DSD_U8 |\ + SNDRV_PCM_FMTBIT_DSD_U16_LE |\ + SNDRV_PCM_FMTBIT_DSD_U32_LE)
static const unsigned int ak4458_rates[] = { 8000, 11025, 16000, 22050, @@ -668,11 +734,13 @@ static const struct regmap_config ak4458_regmap = { static const struct ak4458_drvdata ak4458_drvdata = { .dai_drv = &ak4458_dai, .comp_drv = &soc_codec_dev_ak4458, + .dsd512 = false, };
static const struct ak4458_drvdata ak4497_drvdata = { .dai_drv = &ak4497_dai, .comp_drv = &soc_codec_dev_ak4497, + .dsd512 = true, };
static const struct dev_pm_ops ak4458_pm = { @@ -684,7 +752,6 @@ static const struct dev_pm_ops ak4458_pm = { static int ak4458_i2c_probe(struct i2c_client *i2c) { struct ak4458_priv *ak4458; - const struct ak4458_drvdata *drvdata; int ret, i;
ak4458 = devm_kzalloc(&i2c->dev, sizeof(*ak4458), GFP_KERNEL); @@ -698,7 +765,7 @@ static int ak4458_i2c_probe(struct i2c_client *i2c) i2c_set_clientdata(i2c, ak4458); ak4458->dev = &i2c->dev;
- drvdata = of_device_get_match_data(&i2c->dev); + ak4458->drvdata = of_device_get_match_data(&i2c->dev);
ak4458->reset_gpiod = devm_gpiod_get_optional(ak4458->dev, "reset", GPIOD_OUT_LOW); @@ -720,8 +787,9 @@ static int ak4458_i2c_probe(struct i2c_client *i2c) return ret; }
- ret = devm_snd_soc_register_component(ak4458->dev, drvdata->comp_drv, - drvdata->dai_drv, 1); + ret = devm_snd_soc_register_component(ak4458->dev, + ak4458->drvdata->comp_drv, + ak4458->drvdata->dai_drv, 1); if (ret < 0) { dev_err(ak4458->dev, "Failed to register CODEC: %d\n", ret); return ret; diff --git a/sound/soc/codecs/ak4458.h b/sound/soc/codecs/ak4458.h index f906215f7e4e..9548c5d78621 100644 --- a/sound/soc/codecs/ak4458.h +++ b/sound/soc/codecs/ak4458.h @@ -83,4 +83,7 @@ #define AK4458_ATS_SHIFT 6 #define AK4458_ATS_MASK GENMASK(7, 6)
-#endif /* _AK4458_H */ +#define AK4458_DSDSEL_MASK (0x1 << 0) +#define AK4458_DP_MASK (0x1 << 7) + +#endif
On Fri, Sep 11, 2020 at 04:30:48PM +0800, Shengjiu Wang wrote:
Add a new kcontrol for ak4497 codec for ak4497 has a specific pin selection.
What exactly is this pin selection about - is it something that will change at runtime or should it be configured by the board in the DT?
Hi Mark
On Fri, Sep 11, 2020 at 6:43 PM Mark Brown broonie@kernel.org wrote:
On Fri, Sep 11, 2020 at 04:30:48PM +0800, Shengjiu Wang wrote:
Add a new kcontrol for ak4497 codec for ak4497 has a specific pin selection.
What exactly is this pin selection about - is it something that will change at runtime or should it be configured by the board in the DT?
It is to select which pin group is used for DSD. it won't be changed at runtime. So do you think it is better configured by DT?
best regards wang shengjiu
On Mon, Sep 14, 2020 at 06:47:10PM +0800, Shengjiu Wang wrote:
On Fri, Sep 11, 2020 at 6:43 PM Mark Brown broonie@kernel.org wrote:
What exactly is this pin selection about - is it something that will change at runtime or should it be configured by the board in the DT?
It is to select which pin group is used for DSD. it won't be changed at runtime. So do you think it is better configured by DT?
Yes, DT is a much better fit if it's fixed by the board design.
participants (3)
-
Mark Brown
-
Shengjiu Wang
-
Shengjiu Wang