[PATCH] ASoC: ak4458: Add DSD support for ak4458 and ak4497
ak4458 can't support DSD512 format, but ak4497 can, so add a new variable in ak4458_drvdata to distinguish these two platform.
In hw_params(), calculate bit clock according to different DSD format and configure DSD register.
Signed-off-by: Shengjiu Wang shengjiu.wang@nxp.com --- 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..9054964d5cfa 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_path_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_path_select), + ak4497_dsd_input_path_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 Thu, Sep 10, 2020 at 10:10:32PM +0800, Shengjiu Wang wrote:
ak4458 can't support DSD512 format, but ak4497 can, so add a new variable in ak4458_drvdata to distinguish these two platform.
In hw_params(), calculate bit clock according to different DSD format and configure DSD register.
- SOC_ENUM("AK4497 DSD Data Input Pin", ak4497_dsdp_enum),
This control name doesn't seem to match up with the description - it's talking about a pin but the description sounds like setting a different format for the data?
On Thu, Sep 10, 2020 at 10:23 PM Mark Brown broonie@kernel.org wrote:
On Thu, Sep 10, 2020 at 10:10:32PM +0800, Shengjiu Wang wrote:
ak4458 can't support DSD512 format, but ak4497 can, so add a new variable in ak4458_drvdata to distinguish these two platform.
In hw_params(), calculate bit clock according to different DSD format and configure DSD register.
SOC_ENUM("AK4497 DSD Data Input Pin", ak4497_dsdp_enum),
This control name doesn't seem to match up with the description - it's talking about a pin but the description sounds like setting a different format for the data?
I will add more description. Thanks.
best regards wang shengjiu
participants (3)
-
Mark Brown
-
Shengjiu Wang
-
Shengjiu Wang