All of lore.kernel.org
 help / color / mirror / Atom feed
From: Mark Brown <broonie@kernel.org>
To: Vincent Knecht <vincent.knecht@mailoo.org>
Cc: lgirdwood@gmail.com, robh+dt@kernel.org, perex@perex.cz,
	tiwai@suse.com, stephan@gerhold.net, obayerd@eurocomposant.fr,
	alsa-devel@alsa-project.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, phone-devel@vger.kernel.org,
	~postmarketos/upstreaming@lists.sr.ht
Subject: Re: [PATCH 2/2] ASoC: Add AK4375 support
Date: Mon, 13 Dec 2021 19:07:11 +0000	[thread overview]
Message-ID: <YbeZ37FLnAsMfrDl@sirena.org.uk> (raw)
In-Reply-To: <20211213155914.2558902-2-vincent.knecht@mailoo.org>

[-- Attachment #1: Type: text/plain, Size: 5435 bytes --]

On Mon, Dec 13, 2021 at 04:59:12PM +0100, Vincent Knecht wrote:

> AK4375 is a 32-bit stereo DAC with headphones amplifier.
> There's no documentation for it on akm.com, and only a brief
> datasheet can be found floating on the internets [1].

This driver looks relatively clean but it's in *serious* need of
modernisation, there's issues here that haven't been present upstream
for a very long time.  Fortunately they're mostly style things so it
should be relatively easy to handle.

> +	struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component);
> +	int value = gpiod_get_value_cansleep(ak4375->pdn_gpiod);
> +
> +	if (value < 0)
> +		dev_err(ak4375->dev, "unable to get pdn gpiod: %d\n", value);

You should cache the value set on the GPIO, there's no guarantee that
reads are supported when in output mode or that the value will
correspond to the value present on the pin.  

> +static const struct soc_enum ak4375_dac_enum[] = {
> +	SOC_ENUM_SINGLE(AK4375_0B_LCH_OUTPUT_VOLUME, 7,
> +			ARRAY_SIZE(ak4375_ovolcn_select_texts), ak4375_ovolcn_select_texts),

Magic indexes into an array of enums are error prone and unreasonably
hard to read.  Declare individual variables for each enum like other
CODEC drivers do.

> +static const char * const pdn_on_select[] = { "OFF", "ON" };
> +
> +static const struct soc_enum ak4375_pdn_enum =
> +	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(pdn_on_select), pdn_on_select);

Given that the driver is actively managing this GPIO at runtime with no
coordination with this control this just shouldn't be exposed to
userspace at all, I can't see how userspace can use this control without
breaking the operation of the driver and any time the driver code kicks
in it will just overwrite the current state.

> +static const struct snd_kcontrol_new ak4375_snd_controls[] = {
> +	SOC_SINGLE_TLV("AK4375 Digital Output VolumeL",
> +		       AK4375_0B_LCH_OUTPUT_VOLUME, 0, 0x1f, 0, ovl_tlv),
> +	SOC_SINGLE_TLV("AK4375 Digital Output VolumeR",
> +		       AK4375_0C_RCH_OUTPUT_VOLUME, 0, 0x1f, 0, ovr_tlv),

These should be a standard stereo control - SOC_DOUBLE_R_TLV.

> +	SOC_SINGLE_TLV("AK4375 HP-Amp Analog Volume",
> +		       AK4375_0D_HP_VOLUME_CONTROL, 0, 0x1f, 0, hpg_tlv),

As with other CODEC drivers there is no need to put the name of the
CODEC in every control name.

> +	SOC_ENUM("AK4375 HPL Power-down Resistor", ak4375_dac_enum[6]),
> +	SOC_ENUM("AK4375 HPR Power-down Resistor", ak4375_dac_enum[7]),

These don't sound like things that would vary at runtime, should this be
controlled via DT?

> +static const struct snd_kcontrol_new ak4375_hpl_mixer_controls[] = {
> +	SOC_DAPM_SINGLE("LDACL", AK4375_07_DAC_MONO_MIXING, 0, 1, 0),
> +	SOC_DAPM_SINGLE("RDACL", AK4375_07_DAC_MONO_MIXING, 1, 1, 0),
> +};

The names of on/off switches should end with Switch - see
control-names.rst.

> +static int ak4375_set_pllblock(struct snd_soc_component *component, int fs)
> +{
> +	struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component);
> +	int mclk, pllclk, refclk, pldbit, plmbit, mdivbit, divbit;
> +	u8 mode;

This should be being exposed as a set_pll() operation, or the input to
the PLL configured using set_sysclk().  The input clock to the PLL is
going to be system dependent.

> +static const unsigned int ak4375_rates[] = {
> +	8000, 11025, 16000, 22050,
> +	32000, 44100, 48000, 88200,
> +	96000, 176400, 192000,
> +};
> +
> +static const struct snd_pcm_hw_constraint_list ak4375_rate_constraints = {
> +	.count = ARRAY_SIZE(ak4375_rates),
> +	.list = ak4375_rates,
> +};
> +
> +static int ak4375_startup(struct snd_pcm_substream *substream,
> +			  struct snd_soc_dai *dai)
> +{
> +	return snd_pcm_hw_constraint_list(substream->runtime, 0,
> +					  SNDRV_PCM_HW_PARAM_RATE,
> +					  &ak4375_rate_constraints);
> +}

These are all standard rates, just set the supported rates in the DAI
description rather than using _KNOT.

> +static void ak4375_power_off(struct ak4375_priv *ak4375)
> +{
> +	gpiod_set_value_cansleep(ak4375->pdn_gpiod, 0);
> +	usleep_range(1000, 2000);
> +
> +	if (!IS_ERR(ak4375->avdd_supply))
> +		regulator_disable(ak4375->avdd_supply);

Unless the device is capable of operating without AVDD which doesn't
seem entirely likely use of AVDD should be unconditional.  If that were
the case I'd expect to see some configuration of the device for
operation without the supply which I don't see.

Probably also worth using the _bulk APIs unless the ordering is
important here.

> +	regulator_disable(ak4375->tvdd_supply);
> +
> +	usleep_range(3000, 4000);

What is this sleep doing?

> +	ak4375->pdn_gpiod = devm_gpiod_get_optional(ak4375->dev, "pdn", GPIOD_OUT_LOW);
> +	if (IS_ERR(ak4375->pdn_gpiod))
> +		return PTR_ERR(ak4375->pdn_gpiod);
> +
> +	ret = ak4375_power_on(ak4375);
> +	if (ret < 0)
> +		return ret;

We never unwind this power on - why not just remove the component level
probe/remove?

> +	ret = devm_snd_soc_register_component(ak4375->dev, drvdata->comp_drv,
> +					      drvdata->dai_drv, 1);
> +	if (ret < 0) {
> +		dev_err(ak4375->dev, "Failed to register CODEC: %d\n", ret);
> +		return ret;
> +	}
> +
> +	pm_runtime_enable(&i2c->dev);

Enable runtime PM before probing the component, a card may be
instantiated during component probe and userpace could start using the
card.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

WARNING: multiple messages have this Message-ID (diff)
From: Mark Brown <broonie@kernel.org>
To: Vincent Knecht <vincent.knecht@mailoo.org>
Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org,
	obayerd@eurocomposant.fr, phone-devel@vger.kernel.org,
	stephan@gerhold.net, linux-kernel@vger.kernel.org,
	tiwai@suse.com, lgirdwood@gmail.com, robh+dt@kernel.org,
	~postmarketos/upstreaming@lists.sr.ht
Subject: Re: [PATCH 2/2] ASoC: Add AK4375 support
Date: Mon, 13 Dec 2021 19:07:11 +0000	[thread overview]
Message-ID: <YbeZ37FLnAsMfrDl@sirena.org.uk> (raw)
In-Reply-To: <20211213155914.2558902-2-vincent.knecht@mailoo.org>

[-- Attachment #1: Type: text/plain, Size: 5435 bytes --]

On Mon, Dec 13, 2021 at 04:59:12PM +0100, Vincent Knecht wrote:

> AK4375 is a 32-bit stereo DAC with headphones amplifier.
> There's no documentation for it on akm.com, and only a brief
> datasheet can be found floating on the internets [1].

This driver looks relatively clean but it's in *serious* need of
modernisation, there's issues here that haven't been present upstream
for a very long time.  Fortunately they're mostly style things so it
should be relatively easy to handle.

> +	struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component);
> +	int value = gpiod_get_value_cansleep(ak4375->pdn_gpiod);
> +
> +	if (value < 0)
> +		dev_err(ak4375->dev, "unable to get pdn gpiod: %d\n", value);

You should cache the value set on the GPIO, there's no guarantee that
reads are supported when in output mode or that the value will
correspond to the value present on the pin.  

> +static const struct soc_enum ak4375_dac_enum[] = {
> +	SOC_ENUM_SINGLE(AK4375_0B_LCH_OUTPUT_VOLUME, 7,
> +			ARRAY_SIZE(ak4375_ovolcn_select_texts), ak4375_ovolcn_select_texts),

Magic indexes into an array of enums are error prone and unreasonably
hard to read.  Declare individual variables for each enum like other
CODEC drivers do.

> +static const char * const pdn_on_select[] = { "OFF", "ON" };
> +
> +static const struct soc_enum ak4375_pdn_enum =
> +	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(pdn_on_select), pdn_on_select);

Given that the driver is actively managing this GPIO at runtime with no
coordination with this control this just shouldn't be exposed to
userspace at all, I can't see how userspace can use this control without
breaking the operation of the driver and any time the driver code kicks
in it will just overwrite the current state.

> +static const struct snd_kcontrol_new ak4375_snd_controls[] = {
> +	SOC_SINGLE_TLV("AK4375 Digital Output VolumeL",
> +		       AK4375_0B_LCH_OUTPUT_VOLUME, 0, 0x1f, 0, ovl_tlv),
> +	SOC_SINGLE_TLV("AK4375 Digital Output VolumeR",
> +		       AK4375_0C_RCH_OUTPUT_VOLUME, 0, 0x1f, 0, ovr_tlv),

These should be a standard stereo control - SOC_DOUBLE_R_TLV.

> +	SOC_SINGLE_TLV("AK4375 HP-Amp Analog Volume",
> +		       AK4375_0D_HP_VOLUME_CONTROL, 0, 0x1f, 0, hpg_tlv),

As with other CODEC drivers there is no need to put the name of the
CODEC in every control name.

> +	SOC_ENUM("AK4375 HPL Power-down Resistor", ak4375_dac_enum[6]),
> +	SOC_ENUM("AK4375 HPR Power-down Resistor", ak4375_dac_enum[7]),

These don't sound like things that would vary at runtime, should this be
controlled via DT?

> +static const struct snd_kcontrol_new ak4375_hpl_mixer_controls[] = {
> +	SOC_DAPM_SINGLE("LDACL", AK4375_07_DAC_MONO_MIXING, 0, 1, 0),
> +	SOC_DAPM_SINGLE("RDACL", AK4375_07_DAC_MONO_MIXING, 1, 1, 0),
> +};

The names of on/off switches should end with Switch - see
control-names.rst.

> +static int ak4375_set_pllblock(struct snd_soc_component *component, int fs)
> +{
> +	struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component);
> +	int mclk, pllclk, refclk, pldbit, plmbit, mdivbit, divbit;
> +	u8 mode;

This should be being exposed as a set_pll() operation, or the input to
the PLL configured using set_sysclk().  The input clock to the PLL is
going to be system dependent.

> +static const unsigned int ak4375_rates[] = {
> +	8000, 11025, 16000, 22050,
> +	32000, 44100, 48000, 88200,
> +	96000, 176400, 192000,
> +};
> +
> +static const struct snd_pcm_hw_constraint_list ak4375_rate_constraints = {
> +	.count = ARRAY_SIZE(ak4375_rates),
> +	.list = ak4375_rates,
> +};
> +
> +static int ak4375_startup(struct snd_pcm_substream *substream,
> +			  struct snd_soc_dai *dai)
> +{
> +	return snd_pcm_hw_constraint_list(substream->runtime, 0,
> +					  SNDRV_PCM_HW_PARAM_RATE,
> +					  &ak4375_rate_constraints);
> +}

These are all standard rates, just set the supported rates in the DAI
description rather than using _KNOT.

> +static void ak4375_power_off(struct ak4375_priv *ak4375)
> +{
> +	gpiod_set_value_cansleep(ak4375->pdn_gpiod, 0);
> +	usleep_range(1000, 2000);
> +
> +	if (!IS_ERR(ak4375->avdd_supply))
> +		regulator_disable(ak4375->avdd_supply);

Unless the device is capable of operating without AVDD which doesn't
seem entirely likely use of AVDD should be unconditional.  If that were
the case I'd expect to see some configuration of the device for
operation without the supply which I don't see.

Probably also worth using the _bulk APIs unless the ordering is
important here.

> +	regulator_disable(ak4375->tvdd_supply);
> +
> +	usleep_range(3000, 4000);

What is this sleep doing?

> +	ak4375->pdn_gpiod = devm_gpiod_get_optional(ak4375->dev, "pdn", GPIOD_OUT_LOW);
> +	if (IS_ERR(ak4375->pdn_gpiod))
> +		return PTR_ERR(ak4375->pdn_gpiod);
> +
> +	ret = ak4375_power_on(ak4375);
> +	if (ret < 0)
> +		return ret;

We never unwind this power on - why not just remove the component level
probe/remove?

> +	ret = devm_snd_soc_register_component(ak4375->dev, drvdata->comp_drv,
> +					      drvdata->dai_drv, 1);
> +	if (ret < 0) {
> +		dev_err(ak4375->dev, "Failed to register CODEC: %d\n", ret);
> +		return ret;
> +	}
> +
> +	pm_runtime_enable(&i2c->dev);

Enable runtime PM before probing the component, a card may be
instantiated during component probe and userpace could start using the
card.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

  reply	other threads:[~2021-12-13 19:07 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-12-13 15:59 [PATCH 1/2] ASoC: dt-bindings: codecs: Add bindings for ak4375 Vincent Knecht
2021-12-13 15:59 ` Vincent Knecht
2021-12-13 15:59 ` [PATCH 2/2] ASoC: Add AK4375 support Vincent Knecht
2021-12-13 15:59   ` Vincent Knecht
2021-12-13 19:07   ` Mark Brown [this message]
2021-12-13 19:07     ` Mark Brown
2021-12-14 18:28     ` Vincent Knecht
2021-12-14 18:28       ` Vincent Knecht
2021-12-13 15:59 ` [PATCH 1/2] ASoC: dt-bindings: codecs: Add bindings for ak4375 Vincent Knecht
2021-12-13 15:59   ` Vincent Knecht
2021-12-15 20:20   ` Rob Herring
2021-12-15 20:20     ` Rob Herring
2021-12-13 15:59 ` [PATCH 2/2] ASoC: Add AK4375 support Vincent Knecht
2021-12-13 15:59   ` Vincent Knecht

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=YbeZ37FLnAsMfrDl@sirena.org.uk \
    --to=broonie@kernel.org \
    --cc=alsa-devel@alsa-project.org \
    --cc=devicetree@vger.kernel.org \
    --cc=lgirdwood@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=obayerd@eurocomposant.fr \
    --cc=perex@perex.cz \
    --cc=phone-devel@vger.kernel.org \
    --cc=robh+dt@kernel.org \
    --cc=stephan@gerhold.net \
    --cc=tiwai@suse.com \
    --cc=vincent.knecht@mailoo.org \
    --cc=~postmarketos/upstreaming@lists.sr.ht \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.