All of lore.kernel.org
 help / color / mirror / Atom feed
From: Cezary Rojewski <cezary.rojewski@intel.com>
To: Christian Marangi <ansuelsmth@gmail.com>
Cc: Liam Girdwood <lgirdwood@gmail.com>,
	Mark Brown <broonie@kernel.org>, "Rob Herring" <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	"Conor Dooley" <conor+dt@kernel.org>,
	Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.com>,
	Matthias Brugger <matthias.bgg@gmail.com>,
	"AngeloGioacchino Del Regno"
	<angelogioacchino.delregno@collabora.com>,
	Philipp Zabel <p.zabel@pengutronix.de>,
	Cyril Chao <Cyril.Chao@mediatek.com>,
	Darren Ye <darren.ye@mediatek.com>,
	Daniel Golle <daniel@makrotopia.org>,
	Rosen Penev <rosenp@gmail.com>,
	bui duc phuc <phucduc.bui@gmail.com>,
	<linux-sound@vger.kernel.org>, <devicetree@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>,
	<linux-arm-kernel@lists.infradead.org>,
	<linux-mediatek@lists.infradead.org>
Subject: Re: [PATCH v4 4/4] ASoC: airoha: Add AFE and machine driver for Airoha AN7581
Date: Fri, 31 Jul 2026 10:39:44 +0200	[thread overview]
Message-ID: <c1ac7957-dcfc-42b6-bb24-9aa76f9c334a@intel.com> (raw)
In-Reply-To: <20260731064326.2284228-5-ansuelsmth@gmail.com>

On 7/31/2026 8:43 AM, Christian Marangi wrote:
> Add support for the Sound system present on Airoha AN7581 SoC. This is
> based on the mediatek AFE drivers.
> 
> Also add the machine driver to create an actual sound card for the AFE.

...

> --- /dev/null
> +++ b/sound/soc/mediatek/an7581/an7581-afe-common.h
> @@ -0,0 +1,50 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * an7581-afe-common.h  --  Airoha AN7581 audio driver definitions
> + */
> +
> +#ifndef _AN7581_AFE_COMMON_H_
> +#define _AN7581_AFE_COMMON_H_
> +
> +#include <linux/atomic.h>
> +#include <sound/soc.h>
> +#include <linux/list.h>
> +#include <linux/regmap.h>

Sort these, linux/ comes before sound/.

> +#include "../../mediatek/common/mtk-base-afe.h"
> +
> +enum {
> +	AN7581_MEMIF_DL1,
> +	AN7581_MEMIF_UL1,
> +	AN7581_MEMIF_NUM,
> +	AN7581_DAI_ETDM = AN7581_MEMIF_NUM,
> +	AN7581_DAI_NUM,
> +};
> +
> +enum {
> +	AN7581_IRQ_0,
> +	AN7581_IRQ_1,
> +	AN7581_IRQ_NUM,
> +};
> +
> +struct an7581_memif_irq_desc {
> +	u8 irq;
> +	u32 status_bit;
> +	u32 clear_reg;
> +};
> +
> +struct an7581_afe_private {
> +	unsigned int users;
> +	/* protect concurrent ETDM user tracking */
> +	struct mutex user_lock;
> +
> +	/* dai */
> +	void *dai_priv[AN7581_DAI_NUM];

The comment is redundant.

> +};
> +
> +unsigned int an7581_afe_rate_transform(struct device *dev,
> +				       unsigned int rate);
> +
> +/* dai register */
> +int an7581_dai_etdm_register(struct mtk_base_afe *afe);

Ditto.

> +
> +#endif

...

> +unsigned int an7581_afe_rate_transform(struct device *dev, unsigned int rate)
> +{
> +	switch (rate) {
> +	case 7350:
> +		return ARH_AFE_RATE_7K;
> +	case 8000:
> +		return ARH_AFE_RATE_8K;
> +	case 11025:
> +		return ARH_AFE_RATE_11K;
> +	case 12000:
> +		return ARH_AFE_RATE_12K;
> +	case 14700:
> +		return ARH_AFE_RATE_14K;
> +	case 16000:
> +		return ARH_AFE_RATE_16K;
> +	case 22050:
> +		return ARH_AFE_RATE_22K;
> +	case 24000:
> +		return ARH_AFE_RATE_24K;
> +	case 29400:
> +		return ARH_AFE_RATE_29K;
> +	case 32000:
> +		return ARH_AFE_RATE_32K;
> +	case 44100:
> +		return ARH_AFE_RATE_44K;
> +	case 48000:
> +		return ARH_AFE_RATE_48K;
> +	case 88200:
> +		return ARH_AFE_RATE_88K;
> +	case 96000:
> +		return ARH_AFE_RATE_96K;
> +	case 176400:
> +		return ARH_AFE_RATE_176K;
> +	case 192000:
> +		return ARH_AFE_RATE_192K;
> +	case 352800:
> +		return ARH_AFE_RATE_352K;
> +	case 384000:
> +		return ARH_AFE_RATE_384K;
> +	default:
> +		dev_warn(dev, "%s(), rate %u invalid, using %d!!!\n",
> +			 __func__, rate, ARH_AFE_RATE_48K);

Three '!', really? :)

> +		return ARH_AFE_RATE_48K;
> +	}
> +}

> +static const struct snd_pcm_hardware an7581_afe_hardware = {
> +	.info = SNDRV_PCM_INFO_MMAP |
> +		SNDRV_PCM_INFO_INTERLEAVED |
> +		SNDRV_PCM_INFO_MMAP_VALID,
> +	.formats = SNDRV_PCM_FMTBIT_S16_LE |
> +		   SNDRV_PCM_FMTBIT_S24_LE |
> +		   SNDRV_PCM_FMTBIT_S32_LE,
> +	.period_bytes_min = 512,
> +	.period_bytes_max = 128 * 1024,
> +	.periods_min = 2,
> +	.periods_max = 256,
> +	.buffer_bytes_max = 256 * 1024,
> +	.fifo_size = 0,

static takes care of this unless you want to explicitly state the 
fifo_size must be 0 - in such case comment is necessary, otherwise drop 
the assignment.

> +};
> +
> +static int an7581_memif_fs(struct snd_pcm_substream *substream,
> +			   unsigned int rate)

For the entire file - char limit is 100, there is no reason not to 
utilize it.

> +{
> +	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
> +	struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
> +	struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
> +
> +	return an7581_afe_rate_transform(afe->dev, rate);
> +}
> +
> +static int an7581_irq_fs(struct snd_pcm_substream *substream,
> +			 unsigned int rate)
> +{
> +	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
> +	struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
> +	struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
> +
> +	return an7581_afe_rate_transform(afe->dev, rate);
> +}

These two functions seem to be identical.

> +
> +static int an7581_afe_fe_startup(struct snd_pcm_substream *substream,
> +				 struct snd_soc_dai *dai)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
> +	int ret;
> +
> +	ret = mtk_afe_fe_startup(substream, dai);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
> +		ret = snd_pcm_hw_constraint_minmax(runtime,
> +						   SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
> +						   0x2000, UINT_MAX);
> +		if (ret < 0)
> +			dev_err(afe->dev, "hw_constraint_minmax failed\n");
> +	}
> +
> +	return ret;

mtk_afe_fe_startup() acquires irq via mtk_dynamic_irq_acquire(). Do we 
need to unwind the process if snd_pcm_hw_constraint_minmax() fails?

> +}

...

> +static int an7581_afe_pcm_dev_probe(struct platform_device *pdev)
> +{
> +	struct an7581_afe_private *afe_priv;
> +	struct reset_control *reset;
> +	struct mtk_base_afe *afe;
> +	struct device *dev;
> +	int i, irq_id, ret;
> +	void *base;
> +
> +	afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
> +	if (!afe)
> +		return -ENOMEM;
> +	platform_set_drvdata(pdev, afe);

Typically drvdata is assigned last, one we know probe() is successful. 
Is there some specific reason why you assign the pointer immediately?
> +
> +	afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
> +					  GFP_KERNEL);
> +	if (!afe->platform_priv)
> +		return -ENOMEM;

Especially when the memory allocation is still ongoing..

> +
> +	afe_priv = afe->platform_priv;
> +	mutex_init(&afe_priv->user_lock);
> +	afe->dev = &pdev->dev;
> +	dev = afe->dev;
> +
> +	reset = devm_reset_control_get_exclusive(dev, NULL);
> +	if (IS_ERR(reset))
> +		return PTR_ERR(reset);
> +
> +	/* Global reset I2S */
> +	reset_control_assert(reset);
> +	usleep_range(10, 20);
> +	reset_control_deassert(reset);
> +
> +	afe->base_addr = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(afe->base_addr))
> +		return PTR_ERR(afe->base_addr);
> +
> +	base = devm_platform_ioremap_resource(pdev, 1);
> +	if (IS_ERR(base))
> +		return PTR_ERR(base);
> +
> +	ret = devm_pm_runtime_enable(dev);
> +	if (ret)
> +		return ret;
> +
> +	afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
> +					    &an7581_afe_regmap_config);
> +	if (IS_ERR(afe->regmap))
> +		return PTR_ERR(afe->regmap);
> +
> +	mutex_init(&afe->irq_alloc_lock);
> +
> +	/* irq initialize */
> +	afe->irqs_size = AN7581_IRQ_NUM;
> +	afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs),
> +				 GFP_KERNEL);
> +	if (!afe->irqs)
> +		return -ENOMEM;

If the size is known upfront, I'd just move all the allocations to the top.

> +
> +	for (i = 0; i < afe->irqs_size; i++)
> +		afe->irqs[i].irq_data = &irq_data[i];
> +
> +	afe->irqs[AN7581_IRQ_0].regmap = afe->regmap;
> +	afe->irqs[AN7581_IRQ_1].regmap = devm_regmap_init_mmio(&pdev->dev, base,
> +							       &an7581_afe_irq1_regmap_config);
> +	if (IS_ERR(afe->irqs[AN7581_IRQ_1].regmap))
> +		return PTR_ERR(afe->irqs[AN7581_IRQ_1].regmap);
> +
> +	/* request irq */
> +	irq_id = platform_get_irq(pdev, 0);
> +	if (irq_id < 0)
> +		return irq_id;
> +
> +	/* init memif */
> +	afe->memif_size = AN7581_MEMIF_NUM;
> +	afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif),
> +				  GFP_KERNEL);
> +	if (!afe->memif)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < afe->memif_size; i++) {
> +		int sel_irq = an7581_memif_irq_descs[i].irq;
> +
> +		afe->memif[i].data = &memif_data[i];
> +		afe->memif[i].irq_usage = sel_irq;
> +		afe->memif[i].const_irq = 1;
> +		afe->irqs[sel_irq].irq_occupyed = true;
> +	}
> +
> +	/* init sub_dais */
> +	INIT_LIST_HEAD(&afe->sub_dais);
> +
> +	ret = devm_request_irq(dev, irq_id, an7581_afe_irq_handler,
> +			       IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to request irq for asys-isr\n");
> +
> +	for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) {
> +		ret = dai_register_cbs[i](afe);
> +		if (ret)
> +			return dev_err_probe(dev, ret, "DAI register failed, i: %d\n", i);
> +	}
> +
> +	/* init dai_driver and component_driver */
> +	ret = mtk_afe_combine_sub_dai(afe);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "mtk_afe_combine_sub_dai fail\n");
> +
> +	afe->mtk_afe_hardware = &an7581_afe_hardware;
> +	afe->memif_fs = an7581_memif_fs;
> +	afe->irq_fs = an7581_irq_fs;
> +
> +	/* register component */
> +	ret = devm_snd_soc_register_component(&pdev->dev,
> +					      &mtk_afe_pcm_platform,
> +					      NULL, 0);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Cannot register AFE component\n");
> +
> +	ret = devm_snd_soc_register_component(afe->dev,
> +					      &an7581_afe_pcm_dai_component,
> +					      afe->dai_drivers,
> +					      afe->num_dai_drivers);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Cannot register PCM DAI component\n");
> +
> +	return 0;
> +}

...

> +/* dai ops */
> +static int mtk_dai_etdm_startup(struct snd_pcm_substream *substream,
> +				struct snd_soc_dai *dai)
> +{
> +	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
> +	struct an7581_afe_private *afe_priv = afe->platform_priv;
> +
> +	regmap_set_bits(afe->regmap, AFE_DAC_CON0,
> +			BIT(AFE_AFE_ENABLE_SHIFT));
> +
> +	mutex_lock(&afe_priv->user_lock);

I'd suggest to switch to guards, see cleanup.h.

> +	afe_priv->users++;
> +	mutex_unlock(&afe_priv->user_lock);
> +
> +	return 0;

Why skip the return code of regmap_set_bits()?

> +}
> +
> +static void mtk_dai_etdm_shutdown(struct snd_pcm_substream *substream,
> +				  struct snd_soc_dai *dai)
> +{
> +	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
> +	struct an7581_afe_private *afe_priv = afe->platform_priv;
> +
> +	mutex_lock(&afe_priv->user_lock);
> +	afe_priv->users--;
> +	if (!afe_priv->users)
> +		regmap_clear_bits(afe->regmap, AFE_DAC_CON0,
> +				  BIT(AFE_AFE_ENABLE_SHIFT));
> +	mutex_unlock(&afe_priv->user_lock);

Well, here we clear bits in locked state whereas above they're set 
unlocked. Typically such inconsistency translates to an issue.

> +}
> +
> +static unsigned int get_etdm_ch_fixup(unsigned int channels)
> +{
> +	if (channels > 16)
> +		return 24;
> +	else if (channels > 8)
> +		return 16;
> +	else if (channels > 4)
> +		return 8;
> +	else if (channels > 2)
> +		return 4;
> +	else
> +		return 2;

Refactor, not a single 'else-if' is needed here as well as 'else'.
If a preceding if-statement ends with 'return' there is no elsing anything.

> +}
> +
> +static int mtk_dai_etdm_config(struct mtk_base_afe *afe,
> +			       struct snd_pcm_hw_params *params,
> +			       struct snd_soc_dai *dai,
> +			       int stream)
> +{
> +	struct an7581_afe_private *afe_priv = afe->platform_priv;
> +	struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai->id];
> +	unsigned int rate = params_rate(params);
> +	unsigned int etdm_rate = an7581_etdm_rate_transform(afe->dev, rate);
> +	unsigned int channels = params_channels(params);
> +	unsigned int bit_width = params_width(params);
> +	unsigned int wlen = get_etdm_wlen(bit_width);

All the format variables are used once or twice. I'd drop them. Right 
now you have 11 (!) locals on the stack.

> +	unsigned int val = 0, val1 = 0;
> +	unsigned int mask = 0, mask1 = 0;
> +
> +	dev_dbg(afe->dev, "%s(), stream %d, rate %u, bitwidth %u\n",
> +		__func__, stream, rate, bit_width);
> +
> +	/* CON0 */
> +	mask |= ETDM_SLAVE_MODE;
> +	if (etdm_data->slave_mode)
> +		val |= ETDM_SLAVE_MODE;
> +	mask |= ETDM_BIT_LEN;
> +	val |= FIELD_PREP(ETDM_BIT_LEN, bit_width - 1);
> +	mask |= ETDM_WRD_LEN;
> +	val |= FIELD_PREP(ETDM_WRD_LEN, wlen - 1);
> +	mask |= ETDM_FMT;
> +	val |= FIELD_PREP(ETDM_FMT, etdm_data->format);
> +	mask |= ETDM_CH_NUM;
> +	val |= FIELD_PREP(ETDM_CH_NUM, get_etdm_ch_fixup(channels) - 1);

I'd group 'mask' and 'val' together, easier to follow. These do not seem 
intertwined.

> +
> +	/* CON1 */
> +	mask1 |= EDTM_LRCK_AUTO_MODE;
> +	val1 |= EDTM_LRCK_AUTO_MODE;
> +	mask1 |= EDTM_CKEN_SEL;
> +	val1 |= EDTM_CKEN_SEL;
> +	mask1 |= EDTM_LRCK_AUTO_OFF;
> +	val1 |= EDTM_LRCK_AUTO_OFF;
> +	mask1 |= EDTM_INITIAL_POINT;
> +	val1 |= FIELD_PREP(EDTM_INITIAL_POINT, 14);
> +	mask1 |= EDTM_INITIAL_COUNT;
> +	val1 |= FIELD_PREP(EDTM_INITIAL_COUNT, 14);

Ditto.

> +
> +	switch (stream) {
> +	case SNDRV_PCM_STREAM_PLAYBACK:
> +		/* set ETDM_OUT1_CON0 */
> +		regmap_update_bits(afe->regmap, ETDM_OUT1_CON0, mask, val);
> +
> +		mask1 |= EDTM_DIRECT_INPUT_MASTER_BCK;
> +		val1 |= EDTM_DIRECT_INPUT_MASTER_BCK;
> +
> +		/* set ETDM_OUT1_CON1 */
> +		regmap_update_bits(afe->regmap, ETDM_OUT1_CON1, mask1, val1);
> +
> +		/* set ETDM_OUT1_CON4 */
> +		regmap_update_bits(afe->regmap, ETDM_OUT1_CON4, OUT_SEL_FS,
> +				   FIELD_PREP(OUT_SEL_FS, etdm_rate));
> +
> +		regmap_update_bits(afe->irqs[AN7581_IRQ_0].regmap,
> +				   afe->irqs[AN7581_IRQ_0].irq_data->irq_en_reg,
> +				   AFE_IRQ_EN_SEL, AFE_IRQ_EN_SEL_I2SOUT);
> +		break;
> +	case SNDRV_PCM_STREAM_CAPTURE:
> +		/* set ETDM_IN1_CON0 */
> +		regmap_update_bits(afe->regmap, ETDM_IN1_CON0, mask, val);
> +
> +		/* set ETDM_IN1_CON1 */
> +		regmap_update_bits(afe->regmap, ETDM_IN1_CON1, mask1, val1);
> +
> +		/* set ETDM_IN1_CON3 */

All the comments found in this function do not explain anything. Drop them.

> +		regmap_update_bits(afe->regmap, ETDM_IN1_CON3, IN_SEL_FS,
> +				   FIELD_PREP(IN_SEL_FS, etdm_rate));
> +
> +		regmap_update_bits(afe->irqs[AN7581_IRQ_1].regmap,
> +				   afe->irqs[AN7581_IRQ_1].irq_data->irq_en_reg,
> +				   AFE_IRQ_EN_SEL, AFE_IRQ_EN_SEL_I2SIN);
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static int mtk_dai_etdm_hw_params(struct snd_pcm_substream *substream,
> +				  struct snd_pcm_hw_params *params,
> +				  struct snd_soc_dai *dai)
> +{
> +	unsigned int rate = params_rate(params);
> +	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
> +
> +	regmap_update_bits(afe->regmap, ETDM_COWORK_CON0,
> +			   EDTM_IN1_SLAVE_SEL,
> +			   EDTM_IN1_SLAVE_FROM_ETDMIN1_SLAVE);
> +	regmap_update_bits(afe->regmap, ETDM_COWORK_CON0,
> +			   EDTM_OUT1_SLAVE_SEL,
> +			   EDTM_OUT1_SLAVE_FROM_ETDMOUT1_SLAVE);
> +	regmap_update_bits(afe->regmap, ETDM_COWORK_CON1,
> +			   EDTM_IN1_SDATA0_SEL,
> +			   EDTM_IN1_SDATA0_FROM_PAD);
> +
> +	switch (rate) {
> +	case 7350:
> +	case 8000:
> +	case 11025:
> +	case 12000:
> +	case 14700:
> +	case 16000:
> +	case 22050:
> +	case 24000:
> +	case 29400:
> +	case 32000:
> +	case 44100:
> +	case 48000:
> +	case 88200:
> +	case 96000:
> +	case 176400:
> +	case 192000:
> +	case 352800:
> +	case 384000:
> +		mtk_dai_etdm_config(afe, params, dai, substream->stream);
> +		return 0;

Again, skipping return code of the main procedure. I'd avoid blind 
'return 0'.

> +	default:
> +		dev_err(afe->dev,
> +			"Sample rate %d invalid\n",
> +			rate);
> +		return -EINVAL;
> +	}
> +}

...

> +static int mtk_dai_etdm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> +{
> +	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
> +	struct an7581_afe_private *afe_priv = afe->platform_priv;
> +	struct mtk_dai_etdm_priv *etdm_data;
> +	void *priv_data;
> +
> +	switch (dai->id) {
> +	case AN7581_DAI_ETDM:
> +		break;
> +	default:
> +		dev_warn(afe->dev, "%s(), id %d not support\n",
> +			 __func__, dai->id);

s/support/supported/

Though this entire check looks weird. Why should set_fmt() care about 
DAI's ID? Such check shall be done once, early with the rest of the 
runtime doing just the important stuff.

> +		return -EINVAL;
> +	}
> +
> +	priv_data = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_etdm_priv),
> +				 GFP_KERNEL);
> +	if (!priv_data)
> +		return -ENOMEM;
> +
> +	afe_priv->dai_priv[dai->id] = priv_data;
> +	etdm_data = afe_priv->dai_priv[dai->id];
> +
> +	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> +	case SND_SOC_DAIFMT_I2S:
> +		etdm_data->format = MTK_DAI_ETDM_FORMAT_I2S;
> +		break;
> +	case SND_SOC_DAIFMT_DSP_A:
> +		etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPA;
> +		break;
> +	case SND_SOC_DAIFMT_DSP_B:
> +		etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPB;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}

Please waterfall the points to all the functions/files. I can do another 
round once this batch is addressed or explained first.

Kind regards,
Czarek


      parent reply	other threads:[~2026-07-31  8:40 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-31  6:43 [PATCH v4 0/4] ASoC: Add support for Airoha AN7581 Christian Marangi
2026-07-31  6:43 ` [PATCH v4 1/4] ASoC: dt-bindings: Add Airoha AN7581 AFE Sound card Christian Marangi
2026-07-31  6:43 ` [PATCH v4 2/4] ASoC: dt-bindings: Add Airoha AN7581 AFE with WM8960 Codec schema Christian Marangi
2026-07-31  6:43 ` [PATCH v4 3/4] ASoC: mediatek: common: permit to provide dedicated regmap for irq Christian Marangi
2026-07-31  6:43 ` [PATCH v4 4/4] ASoC: airoha: Add AFE and machine driver for Airoha AN7581 Christian Marangi
2026-07-31  6:58   ` sashiko-bot
2026-07-31  8:39   ` Cezary Rojewski [this message]

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=c1ac7957-dcfc-42b6-bb24-9aa76f9c334a@intel.com \
    --to=cezary.rojewski@intel.com \
    --cc=Cyril.Chao@mediatek.com \
    --cc=angelogioacchino.delregno@collabora.com \
    --cc=ansuelsmth@gmail.com \
    --cc=broonie@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=daniel@makrotopia.org \
    --cc=darren.ye@mediatek.com \
    --cc=devicetree@vger.kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=lgirdwood@gmail.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mediatek@lists.infradead.org \
    --cc=linux-sound@vger.kernel.org \
    --cc=matthias.bgg@gmail.com \
    --cc=p.zabel@pengutronix.de \
    --cc=perex@perex.cz \
    --cc=phucduc.bui@gmail.com \
    --cc=robh@kernel.org \
    --cc=rosenp@gmail.com \
    --cc=tiwai@suse.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 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.