Linux Sound subsystem development
 help / color / mirror / Atom feed
From: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>
To: Zhang Yi <zhangyi@everest-semi.com>,
	broonie@kernel.org, tiwai@suse.com, linux-sound@vger.kernel.org
Cc: peter.ujfalusi@linux.intel.com, yung-chuan.liao@linux.intel.com,
	ranjani.sridharan@linux.intel.com, kai.vehmanen@linux.intel.com,
	ckeepax@opensource.cirrus.com
Subject: Re: [PATCH v11 3/5] ASoC: es9356-sdca: Add ES9356 SDCA driver
Date: Wed, 6 May 2026 14:26:34 +0200	[thread overview]
Message-ID: <0df21295-d3e3-4f53-8d22-dc28244dc4f1@linux.dev> (raw)
In-Reply-To: <20260506025447.3288-4-zhangyi@everest-semi.com>

Looks mostly good, only a couple of comments below:

> +static int es9356_set_jack_detect(struct snd_soc_component *component,
> +	struct snd_soc_jack *hs_jack, void *data)
> +{
> +	struct es9356_sdw_priv *es9356 = snd_soc_component_get_drvdata(component);
> +	int ret;
> +
> +	es9356->hs_jack = hs_jack;

Other codec drivers have this before the pm_runtime_resume_and_get():

	/* we can only resume if the device was initialized at least once */
	if (!rt711->first_hw_init)
		return 0;

	ret = pm_runtime_resume_and_get(component->dev);


> +	ret = pm_runtime_resume_and_get(component->dev);
> +	if (ret < 0) {
> +		if (ret != -EACCES) {
> +			dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret);
> +			return ret;
> +		}
> +		/* pm_runtime not enabled yet */
> +		dev_info(component->dev, "%s: skipping jack init for now\n", __func__);
> +		return 0;
> +	}
> +
> +	if (es9356->hs_jack)
> +		sdw_write_no_pm(es9356->slave, SDW_SCP_SDCA_INTMASK1,
> +			(SDW_SCP_SDCA_INTMASK_SDCA_7 | SDW_SCP_SDCA_INTMASK_SDCA_5 | SDW_SCP_SDCA_INTMASK_SDCA_1));
> +
> +	pm_runtime_mark_last_busy(component->dev);
> +	pm_runtime_put_autosuspend(component->dev);
> +
> +	return 0;


> +static int es9356_power_state(struct snd_soc_dai *dai, unsigned char *func, unsigned char *pde_entity,
> +	unsigned char *cs_entity, int power)
> +{
> +	struct snd_soc_component *component = dai->component;
> +	struct es9356_sdw_priv *es9356 = snd_soc_component_get_drvdata(component);
> +	unsigned char ps0 = 0x0, ps3 = 0x3;
> +	int ret;
> +
> +	switch (dai->id) {
> +	case ES9356_DMIC:
> +		*func = FUNC_NUM_MIC;
> +		*cs_entity = ES9356_SDCA_ENT_CS113;
> +		*pde_entity = ES9356_SDCA_ENT_PDE11;
> +		break;
> +	case ES9356_AMP:
> +		*func = FUNC_NUM_AMP;
> +		*cs_entity = ES9356_SDCA_ENT_CS21;
> +		*pde_entity = ES9356_SDCA_ENT_PDE23;
> +		break;
> +	case ES9356_JACK_IN:
> +		*func = FUNC_NUM_UAJ;
> +		*cs_entity = ES9356_SDCA_ENT_CS36;
> +		*pde_entity = ES9356_SDCA_ENT_PDE34;
> +		break;
> +	case ES9356_JACK_OUT:
> +		*func = FUNC_NUM_UAJ;
> +		*cs_entity = ES9356_SDCA_ENT_CS41;
> +		*pde_entity = ES9356_SDCA_ENT_PDE47;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	/* power state changes are not independent across functions */
> +	mutex_lock(&es9356->pde_lock);
> +	ret = es9356_pde_transition_delay(es9356, *func, *pde_entity, power?ps3:ps0);
> +	if (ret) {
> +		regmap_write(es9356->regmap,
> +			SDW_SDCA_CTL(*func, *pde_entity, ES9356_SDCA_CTL_REQ_POWER_STATE, 0), power?ps0:ps3);
> +		es9356_pde_transition_delay(es9356, *func, *pde_entity, power?ps0:ps3);
> +	} else
> +		dev_dbg(component->dev, "%s PDE is already %d\n", __func__, power?ps3:ps0);

why do you need to pass a 'power' argument that you then translate as a power state. just pass the desired power state as argument...

> +
> +	mutex_unlock(&es9356->pde_lock);
> +	return 0;
> +}
> +
> +static int es9356_sdw_pcm_hw_params(struct snd_pcm_substream *substream,
> +				    struct snd_pcm_hw_params *params,
> +				    struct snd_soc_dai *dai)
> +{
> +	struct snd_soc_component *component = dai->component;
> +	struct es9356_sdw_priv *es9356 = snd_soc_component_get_drvdata(component);
> +	struct sdw_stream_config stream_config = {0};
> +	struct sdw_port_config port_config = {0};
> +	struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
> +	unsigned char func, pde_entity, cs_entity;
> +	unsigned int rate;
> +	int ret;
> +
> +	if (!sdw_stream)
> +		return -EINVAL;
> +
> +	if (!es9356->slave)
> +		return -EINVAL;
> +
> +	/* SoundWire specific configuration */
> +	snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
> +
> +	port_config.num = dai->id;
> +
> +	ret = sdw_stream_add_slave(es9356->slave, &stream_config,
> +				   &port_config, 1, sdw_stream);
> +	if (ret) {
> +		dev_err(dai->dev, "Unable to configure port\n");
> +		return -EINVAL;
> +	}
> +
> +	switch (params_rate(params)) {
> +	case 16000:
> +		rate = ES9356_SDCA_RATE_16000HZ;
> +		break;
> +	case 44100:
> +		rate = ES9356_SDCA_RATE_44100HZ;
> +		break;
> +	case 48000:
> +		rate = ES9356_SDCA_RATE_48000HZ;
> +		break;
> +	case 96000:
> +		rate = ES9356_SDCA_RATE_96000HZ;
> +		break;
> +	default:
> +		dev_err(component->dev, "%s: Rate %d is not supported\n",
> +			__func__, params_rate(params));
> +		return -EINVAL;
> +	}
> +
> +	ret = es9356_power_state(dai, &func, &pde_entity, &cs_entity, 1);

nit-pick: pde_entity is not used in this routine.

> +	if (ret) {
> +		dev_err(component->dev, "%s: Invalid dai id: %d\n",
> +			__func__, dai->id);
> +		return -EINVAL;
> +	}
> +
> +	regmap_write(es9356->regmap,
> +			SDW_SDCA_CTL(func, cs_entity, ES9356_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), rate);
> +
> +	return 0;
> +}
> +
> +static int es9356_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
> +				  struct snd_soc_dai *dai)
> +{
> +	struct snd_soc_component *component = dai->component;
> +	struct es9356_sdw_priv *es9356 = snd_soc_component_get_drvdata(component);
> +	struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
> +	unsigned char func, pde_entity, cs_entity;
> +	int ret;
> +
> +	if (!es9356->slave)
> +		return -EINVAL;
> +
> +	sdw_stream_remove_slave(es9356->slave, sdw_stream);
> +
> +	ret = es9356_power_state(dai, &func, &pde_entity, &cs_entity, 0);

nit-pick: this helper is a bit weird, it returns information for function, pde and cs that aren't used/necessary here.

> +	if (ret) {
> +		dev_err(component->dev, "%s: Invalid dai id: %d\n",
> +			__func__, dai->id);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}

> +static int es9356_sdca_io_init(struct device *dev, struct sdw_slave *slave)
> +{
> +	struct es9356_sdw_priv *es9356 = dev_get_drvdata(&slave->dev);
> +
> +	if (es9356->hw_init)
> +		return 0;
> +
> +	es9356->disable_irq = false;
> +
> +	regcache_cache_only(es9356->regmap, false);
> +
> +	if (es9356->first_hw_init) {
> +		regcache_cache_bypass(es9356->regmap, true);
> +	} else {
> +		/*
> +		 * PM runtime is only enabled when a Slave reports as Attached
> +		 */
> +
> +		/* set autosuspend parameters */
> +		pm_runtime_set_autosuspend_delay(&slave->dev, 3000);
> +		pm_runtime_use_autosuspend(&slave->dev);
> +
> +		/* update count of parent 'active' children */
> +		pm_runtime_set_active(&slave->dev);
> +
> +		/* make sure the device does not suspend immediately */
> +		pm_runtime_mark_last_busy(&slave->dev);
> +
> +		pm_runtime_enable(&slave->dev);

no, all the initializations should be in the probe, only the set_active() belongs here. see other codecs for inspiration.

> +
> +		es9356_register_init(es9356);
> +	}
> +	pm_runtime_get_noresume(&slave->dev);
> +
> +	regmap_write(es9356->regmap,
> +		SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_XU12, ES9356_SDCA_CTL_SELECTED_MODE, 0), 0x01);
> +	regmap_write(es9356->regmap,
> +		SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT0, ES9356_SDCA_CTL_FUNC_STATUS, 0), 0x40);
> +	regmap_write(es9356->regmap,
> +		SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_XU22, ES9356_SDCA_CTL_SELECTED_MODE, 0), 0x01);
> +	regmap_write(es9356->regmap,
> +		SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT0, ES9356_SDCA_CTL_FUNC_STATUS, 0), 0x40);
> +	regmap_write(es9356->regmap,
> +		SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_XU42, ES9356_SDCA_CTL_SELECTED_MODE, 0), 0x01);
> +	regmap_write(es9356->regmap,
> +		SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_XU36, ES9356_SDCA_CTL_SELECTED_MODE, 0), 0x01);
> +	regmap_write(es9356->regmap,
> +		SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT0, ES9356_SDCA_CTL_FUNC_STATUS, 0), 0x40);
> +
> +	if (es9356->first_hw_init) {
> +		regcache_cache_bypass(es9356->regmap, false);
> +		regcache_mark_dirty(es9356->regmap);
> +	} else
> +		es9356->first_hw_init = true;
> +
> +	es9356->hw_init = true;
> +
> +	pm_runtime_mark_last_busy(&slave->dev);
> +	pm_runtime_put_autosuspend(&slave->dev);
> +
> +	return 0;
> +}

> +static int es9356_sdw_probe(struct sdw_slave *slave,
> +				const struct sdw_device_id *id)
> +{
> +	struct regmap *regmap;
> +
> +	regmap = devm_regmap_init_sdw_mbq_cfg(&slave->dev, slave, &es9356_sdca_regmap, &es9356_mbq_config);
> +	if (IS_ERR(regmap))
> +		return PTR_ERR(regmap);
> +
> +	return es9356_sdca_init(&slave->dev, regmap, slave);
> +}
> +
> +static int es9356_sdw_remove(struct sdw_slave *slave)
> +{
> +	struct es9356_sdw_priv *es9356 = dev_get_drvdata(&slave->dev);
> +
> +	if (es9356->hw_init) {
> +		cancel_delayed_work_sync(&es9356->interrupt_handle_work);
> +		cancel_delayed_work_sync(&es9356->button_detect_work);
> +	}
> +
> +	if (es9356->first_hw_init)
> +		pm_runtime_disable(&slave->dev);

the pm_runtime_enable() should be in the probe() for symmetry, see comment above.

> +
> +	mutex_destroy(&es9356->disable_irq_lock);
> +	mutex_destroy(&es9356->pde_lock);
> +
> +	return 0;
> +}
> +
> +static const struct sdw_device_id es9356_sdw_id[] = {
> +	SDW_SLAVE_ENTRY_EXT(0x04b3, 0x9356, 0x02, 0, 0),
> +	SDW_SLAVE_ENTRY_EXT(0x04b3, 0x9356, 0x03, 0, 0),
> +	{},
> +};
> +MODULE_DEVICE_TABLE(sdw, es9356_sdw_id);
You also need a separate patch cc: Vinod in drivers/soundwire/intel_auxdevice.c to update this:

static struct wake_capable_part wake_capable_list[] = {


  reply	other threads:[~2026-05-06 12:47 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-06  2:54 [PATCH v11 0/5] Add es9356 focused SoundWire CODEC Zhang Yi
2026-05-06  2:54 ` [PATCH v11 1/5] ASoC: sdw_utils: add soc_sdw_es9356 Zhang Yi
2026-05-06  2:54 ` [PATCH v11 2/5] ASoC: sdw_utils: add ES9356 in codec_info_list Zhang Yi
2026-05-06  2:54 ` [PATCH v11 3/5] ASoC: es9356-sdca: Add ES9356 SDCA driver Zhang Yi
2026-05-06 12:26   ` Pierre-Louis Bossart [this message]
2026-05-06  2:54 ` [PATCH v11 4/5] ASoC: Intel: soc-acpi: arl: Add es9356 support Zhang Yi
2026-05-06  2:54 ` [PATCH v11 5/5] ASoC: Intel: sof_sdw: add " Zhang Yi

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=0df21295-d3e3-4f53-8d22-dc28244dc4f1@linux.dev \
    --to=pierre-louis.bossart@linux.dev \
    --cc=broonie@kernel.org \
    --cc=ckeepax@opensource.cirrus.com \
    --cc=kai.vehmanen@linux.intel.com \
    --cc=linux-sound@vger.kernel.org \
    --cc=peter.ujfalusi@linux.intel.com \
    --cc=ranjani.sridharan@linux.intel.com \
    --cc=tiwai@suse.com \
    --cc=yung-chuan.liao@linux.intel.com \
    --cc=zhangyi@everest-semi.com \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox