[PATCH v3] ASoC: adds component driver for TAS575xM digital amplifiers
Cezary Rojewski
cezary.rojewski at intel.com
Mon Jan 10 17:25:14 CET 2022
On 2022-01-10 9:45 AM, Joerg Schambacher wrote:
> Adds a minimum component driver to run the amplifier in I2S master
> mode only from standard audio clocks. Therefore, it only allows
> 44.1, 88.2, 176.4, 48, 96 and 192ksps with 16, 20, 24 and 32 bits
> sample size. Digital volume control and the -6dB and +0.8dB switches
> are supported.
Couple nitpicks and suggestions below.
(...)
> +static int tas5754m_set_bias_level(struct snd_soc_component *component,
> + enum snd_soc_bias_level level)
> +{
> + struct tas5754m_priv *tas5754m =
> + snd_soc_component_get_drvdata(component);
> + int ret;
> +
> + switch (level) {
> + case SND_SOC_BIAS_ON:
> + case SND_SOC_BIAS_PREPARE:
> + break;
> +
> + case SND_SOC_BIAS_STANDBY:
> + ret = regmap_update_bits(tas5754m->regmap,
> + TAS5754M_POWER, TAS5754M_RQST, 0);
> + if (ret != 0) {
I believe we are dealing here with standard API function i.e. 0 on
success and negative value on error. And thus, 'if (ret)' suffices.
> + dev_err(component->dev,
> + "Failed to remove standby: %d\n", ret);
> + return ret;
> + }
> + break;
> +
> + case SND_SOC_BIAS_OFF:
> + ret = regmap_update_bits(tas5754m->regmap,
> + TAS5754M_POWER, TAS5754M_RQST, TAS5754M_RQST);
> + if (ret != 0) {
Ditto. This also goes for every single usage of regmap_xxx() in this file.
> + dev_err(component->dev,
> + "Failed to request standby: %d\n", ret);
> + return ret;
> + }
> + break;
> + }
> +
> + return 0;
You could also drop the 'return ret' from the if-statements above -
granting you also ability to drop the brackets - and instead return
'ret' instead of '0' here. Of course that means 'ret' needs to be
initialized appropriately at the top of the function.
> +}
> +
> +int tas5754m_set_clock_tree_master(struct snd_soc_dai *dai,
> + struct snd_pcm_hw_params *params)
Indentation seems off.
> +{
> + struct snd_soc_component *component = dai->component;
> + struct tas5754m_priv *tas5754m =
> + snd_soc_component_get_drvdata(component);
> + static const struct reg_sequence pll_settings[] = {
> + { TAS5754M_PLL_COEFF_P, 0x01 }, // P=2
> + { TAS5754M_PLL_COEFF_J, 0x08 }, // J=8
> + { TAS5754M_PLL_COEFF_DL, 0x00 }, // D12-8 = 0
> + { TAS5754M_PLL_COEFF_DH, 0x00 }, // D7-0 = 0
> + { TAS5754M_PLL_COEFF_R, 0x00 }, // R=1
> + };
> + int ret;
> +
> + /* disable PLL before any clock tree change */
> + ret = regmap_update_bits(tas5754m->regmap, TAS5754M_PLL_EN,
> + TAS5754M_PLLE, 0);
> + if (ret != 0) {
> + dev_err(component->dev, "Failed to disable PLL: %d\n", ret);
> + return ret;
> + }
> +
> + /* set DAC clock source to MCLK */
> + ret = regmap_write(tas5754m->regmap, TAS5754M_DAC_REF, 0x30);
> + if (ret != 0) {
> + dev_err(component->dev, "Failed to set DAC ref\n");
> + return ret;
> + }
> +
> + /* run PLL at fixed ratio to MCLK */
> + ret = regmap_multi_reg_write(tas5754m->regmap, pll_settings,
> + ARRAY_SIZE(pll_settings));
> + if (ret != 0) {
> + dev_err(component->dev, "Failed to set PLL ratio\n");
> + return ret;
> + }
> +
> + /* set DSP divider to 2 => reg 0x01 */
> + ret = regmap_write(tas5754m->regmap, TAS5754M_DSP_CLKDIV, 1);
> + if (ret != 0) {
> + dev_err(component->dev, "Failed to set DSP divider\n");
> + return ret;
> + }
> + /* set DAC divider to 4 => reg 0x03*/
> + ret = regmap_write(tas5754m->regmap, TAS5754M_DAC_CLKDIV, 3);
> + if (ret != 0) {
> + dev_err(component->dev, "Failed to set OSDACR divider\n");
> + return ret;
> + }
> + /* set OSR divider to 1 */
> + ret = regmap_write(tas5754m->regmap, TAS5754M_OSR_CLKDIV, 0);
> + if (ret != 0) {
> + dev_err(component->dev, "Failed to set OSR divider\n");
> + return ret;
> + }
> + /* set CP divider to 4 => reg 0x03*/
> + ret = regmap_write(tas5754m->regmap, TAS5754M_NCP_CLKDIV, 3);
> + if (ret != 0) {
> + dev_err(component->dev, "Failed to set CP divider\n");
> + return ret;
> + }
> + /* finally enable PLL */
> + ret = regmap_update_bits(tas5754m->regmap, TAS5754M_PLL_EN,
> + TAS5754M_PLLE, 1);
> + if (ret != 0) {
> + dev_err(component->dev, "Failed to enable PLL: %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
I'd suggest to keep the logical block organization cohesive. Especially
if there are several of them all located within a single function. Some
of the do/check/error-out blocks above are separated by a newline from
the following ones, and some are not.
Another point is the cohesiveness of the error-message format. Some of
the above print value of 'ret' i.e. carry additional value whereas other
skip that part. Is this intentional?
> +
> +int tas5754m_set_dai_mode(struct snd_soc_dai *dai)
> +{
> + struct snd_soc_component *component = dai->component;
> + struct tas5754m_priv *tas5754m =
> + snd_soc_component_get_drvdata(component);
> + int fmt = tas5754m->fmt;
> +
> + /* only I2S MASTER mode implemented */
> + if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S)) {
Maybe I'm missing something but the most outter pair of brackets is
redundant.
> + dev_err(component->dev,
> + "DAI format not supported (I2S master only)\n");
> + return -EINVAL;
> + }
> + /* TAS5754/6m do not support inverted clocks in MASTER mode */
A newline before the comment would make this more readabile - that's a
new logical block afterall.
> + if (((fmt & SND_SOC_DAIFMT_CLOCK_MASK) != SND_SOC_DAIFMT_NB_NF)) {
Again, I may be missing something, but this looks like outter brackets
are redundant.
> + dev_err(component->dev, "Inverted clocks not supported\n");
> + return -EINVAL;
> + }
> +
> + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
> + case SND_SOC_DAIFMT_CBM_CFM:
> + regmap_update_bits(tas5754m->regmap,
> + TAS5754M_BCLK_LRCLK_CFG,
> + TAS5754M_LRKO | TAS5754M_BCKO,
> + TAS5754M_LRKO | TAS5754M_BCKO);
> + /* reset CLK dividers */
> + regmap_update_bits(tas5754m->regmap,
> + TAS5754M_MASTER_MODE,
> + 0x00,
> + TAS5754M_RLRK | TAS5754M_RBCK);
> + /* ignore all clock error detection but MCLK */
> + regmap_update_bits(tas5754m->regmap,
> + TAS5754M_ERROR_DETECT,
> + TAS5754M_IPLK | TAS5754M_DCAS |
> + TAS5754M_IDCM | TAS5754M_IDSK |
> + TAS5754M_IDBK | TAS5754M_IDFS,
> + TAS5754M_IPLK | TAS5754M_DCAS |
> + TAS5754M_IDCM | TAS5754M_IDSK |
> + TAS5754M_IDBK | TAS5754M_IDFS);
> + break;
> + case SND_SOC_DAIFMT_CBS_CFS:
> + case SND_SOC_DAIFMT_CBM_CFS:
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +int tas5754m_set_dividers_master(struct snd_soc_dai *dai,
> + struct snd_pcm_hw_params *params)
> +{
> + struct snd_soc_component *component = dai->component;
> + struct tas5754m_priv *tas5754m =
> + snd_soc_component_get_drvdata(component);
> + unsigned long bclk;
> + unsigned long mclk;
> + int bclk_div;
> + int lrclk_div;
> + int osr;
> + int ret;
> +
> + mclk = clk_get_rate(tas5754m->sclk);
> + bclk = tas5754m->sample_len * 2 * params_rate(params);
> + bclk_div = mclk / bclk;
> + lrclk_div = tas5754m->sample_len * 2;
> + osr = mclk / 4 / params_rate(params) / 16;
Is there a specific reason as to why these magic numbers aren't
defines/constants?
> +
> + // stop LR / SCLK clocks
Formatting of this comment looks odd. Please align with the recommended one.
(...)
Regards,
Czarek
More information about the Alsa-devel
mailing list