mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v3 0/7] ASoC: codecs: aw88261: fixes and cleanup
@ 2026-05-29 20:05 Val Packett
  2026-05-29 20:05 ` [PATCH v3 1/7] ASoC: codecs: aw88261: support changing sample rate and bit width Val Packett
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Val Packett @ 2026-05-29 20:05 UTC (permalink / raw)
  Cc: Val Packett, Liam Girdwood, Mark Brown, Jaroslav Kysela,
	Takashi Iwai, Luca Weiss, ~postmarketos/upstreaming, phone-devel,
	linux-sound, linux-kernel

The Awinic smart speaker/amp drivers were merged in a very
"downstream-brained" state, where configuration was only really
determined by the binary "firmware" (register list) file instead
of properly participating in the ASoC system. Let's start
untangling this mess. This series makes aw88261 actually usable
on devices like fairphone-fp5, motorola-dubai and xiaomi-pipa.

v3:
- Set default volume to the halfway point
- Restored mute-until-powered-on behavior
- Made TDM slot_width override the hw_params physical_width
- Made set_tdm_slot(0) reset channel mapping to I2S default (L/R == 0/1)
- Pulled T-b tag

v2: https://lore.kernel.org/all/20260518220906.347958-1-val@packett.cool/
v1: https://lore.kernel.org/all/20260420213250.215465-2-val@packett.cool/

Val Packett (7):
  ASoC: codecs: aw88261: support changing sample rate and bit width
  ASoC: codecs: aw88261: add TDM support
  ASoC: codecs: aw88261: reduce log spam
  ASoC: codecs: aw88261: remove fade in/out on start/stop
  ASoC: codecs: aw88261: remove async start
  ASoC: codecs: aw88261: fix incorrect masks for boost regs
  ASoC: codecs: aw88261: make volume control usable

 sound/soc/codecs/aw88261.c | 627 ++++++++++++++++++++-----------------
 sound/soc/codecs/aw88261.h | 185 ++++++++++-
 2 files changed, 516 insertions(+), 296 deletions(-)

-- 
2.53.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v3 1/7] ASoC: codecs: aw88261: support changing sample rate and bit width
  2026-05-29 20:05 [PATCH v3 0/7] ASoC: codecs: aw88261: fixes and cleanup Val Packett
@ 2026-05-29 20:05 ` Val Packett
  2026-05-29 20:05 ` [PATCH v3 2/7] ASoC: codecs: aw88261: add TDM support Val Packett
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Val Packett @ 2026-05-29 20:05 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai, Weidong Wang
  Cc: Val Packett, Luca Weiss, ~postmarketos/upstreaming, phone-devel,
	linux-sound, linux-kernel

The aw88261 driver only worked with 32-bit 48kHz streams so far due to
the lack of a proper PLL initialization sequence. Fix by selecting all
the necessary PLL settings based on what was passed to us by the
hw_params/set_fmt ops. This replaces the strange downstream routine
that tries two divider modes in sequence.

Fixes: 028a2ae25691 ("ASoC: codecs: Add aw88261 amplifier driver")
Tested-by: Luca Weiss <luca.weiss@fairphone.com> # qcm6490-fairphone-fp5
Signed-off-by: Val Packett <val@packett.cool>
---
 sound/soc/codecs/aw88261.c | 247 +++++++++++++++++++++++++++----------
 sound/soc/codecs/aw88261.h | 116 ++++++++++++++++-
 2 files changed, 299 insertions(+), 64 deletions(-)

diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c
index 50521dd2ebb1..6d4b911be298 100644
--- a/sound/soc/codecs/aw88261.c
+++ b/sound/soc/codecs/aw88261.c
@@ -13,6 +13,7 @@
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <sound/soc.h>
+#include <sound/pcm_params.h>
 #include "aw88261.h"
 #include "aw88395/aw88395_data_type.h"
 #include "aw88395/aw88395_device.h"
@@ -158,7 +159,7 @@ static int aw88261_dev_get_iis_status(struct aw_device *aw_dev)
 	return ret;
 }
 
-static int aw88261_dev_check_mode1_pll(struct aw_device *aw_dev)
+static int aw88261_dev_check_pll(struct aw_device *aw_dev)
 {
 	int ret, i;
 
@@ -175,71 +176,48 @@ static int aw88261_dev_check_mode1_pll(struct aw_device *aw_dev)
 	return -EPERM;
 }
 
-static int aw88261_dev_check_mode2_pll(struct aw_device *aw_dev)
-{
-	unsigned int reg_val;
-	int ret, i;
-
-	ret = regmap_read(aw_dev->regmap, AW88261_PLLCTRL1_REG, &reg_val);
-	if (ret)
-		return ret;
-
-	reg_val &= (~AW88261_CCO_MUX_MASK);
-	if (reg_val == AW88261_CCO_MUX_DIVIDED_VALUE) {
-		dev_dbg(aw_dev->dev, "CCO_MUX is already divider");
-		return -EPERM;
-	}
-
-	/* change mode2 */
-	ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG,
-			~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_DIVIDED_VALUE);
-	if (ret)
-		return ret;
-
-	for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
-		ret = aw88261_dev_get_iis_status(aw_dev);
-		if (ret) {
-			dev_err(aw_dev->dev, "mode2 iis signal check error");
-			usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
-		} else {
-			break;
-		}
-	}
-
-	/* change mode1 */
-	ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG,
-			~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_BYPASS_VALUE);
-	if (ret == 0) {
-		usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
-		for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
-			ret = aw88261_dev_check_mode1_pll(aw_dev);
-			if (ret) {
-				dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error");
-				usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
-			} else {
-				break;
-			}
-		}
-	}
-
-	return ret;
-}
-
-static int aw88261_dev_check_syspll(struct aw_device *aw_dev)
+static int aw88261_dev_configure_syspll(struct aw88261 *aw88261)
 {
+	struct aw_device *aw_dev = aw88261->aw_pa;
 	int ret;
 
-	ret = aw88261_dev_check_mode1_pll(aw_dev);
-	if (ret) {
-		dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check");
-		ret = aw88261_dev_check_mode2_pll(aw_dev);
-		if (ret) {
-			dev_err(aw_dev->dev, "mode2 check iis failed");
-			return ret;
-		}
-	}
+	/* PLL divider must be used for 8/16/32 kHz modes */
+	ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG,
+			~AW88261_CCO_MUX_MASK, aw88261->cco_mux_value);
+	if (ret)
+		return ret;
 
-	return ret;
+	/* The word clock (WCK) defines the beginning of a frame */
+	ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG,
+			~AW88261_I2SSR_MASK, aw88261->sr_value);
+	if (ret)
+		return ret;
+
+	/* The bit clock (BCK) defines the length of a frame */
+	ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG,
+			~AW88261_I2SBCK_MASK, aw88261->bck_value);
+	if (ret)
+		return ret;
+
+	/* The logical frame size is the width of data for 1 slot */
+	ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG,
+			~AW88261_I2SFS_MASK, aw88261->fs_value);
+	if (ret)
+		return ret;
+
+	/* The I2S interface mode (Philips standard, LSB/MSB justified) */
+	ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG,
+			~AW88261_I2SMD_MASK, aw88261->md_value);
+	if (ret)
+		return ret;
+
+	/* The polarity of the bit clock (BCK) */
+	ret = regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+			~AW88261_BCKINV_MASK, aw88261->bck_inv_value);
+	if (ret)
+		return ret;
+
+	return aw88261_dev_check_pll(aw_dev);
 }
 
 static int aw88261_dev_check_sysst(struct aw_device *aw_dev)
@@ -558,7 +536,7 @@ static int aw88261_dev_start(struct aw88261 *aw88261)
 	aw88261_dev_pwd(aw_dev, false);
 	usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
 
-	ret = aw88261_dev_check_syspll(aw_dev);
+	ret = aw88261_dev_configure_syspll(aw88261);
 	if (ret) {
 		dev_err(aw_dev->dev, "pll check failed cannot start");
 		goto pll_check_fail;
@@ -712,6 +690,140 @@ static void aw88261_start(struct aw88261 *aw88261, bool sync_start)
 			AW88261_START_WORK_DELAY_MS);
 }
 
+static int aw88261_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_component *component = dai->component;
+	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		aw88261->bck_inv_value = AW88261_BCKINV_NOT_INVERT_VALUE;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		aw88261->bck_inv_value = AW88261_BCKINV_INVERTED_VALUE;
+		break;
+	default:
+		dev_err(aw88261->aw_pa->dev, "unsupported invert mode 0x%x\n",
+			fmt & SND_SOC_DAIFMT_INV_MASK);
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		aw88261->md_value = AW88261_I2SMD_PHILIPS_STANDARD_VALUE;
+		break;
+	case SND_SOC_DAIFMT_MSB:
+		aw88261->md_value = AW88261_I2SMD_MSB_JUSTIFIED_VALUE;
+		break;
+	case SND_SOC_DAIFMT_LSB:
+		aw88261->md_value = AW88261_I2SMD_LSB_JUSTIFIED_VALUE;
+		break;
+	default:
+		dev_err(aw88261->aw_pa->dev, "unsupported DAI format 0x%x\n",
+			fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int aw88261_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 aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		return 0;
+
+	aw88261->cco_mux_value = AW88261_CCO_MUX_BYPASS_VALUE;
+	switch (params_rate(params)) {
+	case 8000:
+		aw88261->sr_value = AW88261_I2SSR_8KHZ_VALUE;
+		aw88261->cco_mux_value = AW88261_CCO_MUX_DIVIDED_VALUE;
+		break;
+	case 11025:
+		aw88261->sr_value = AW88261_I2SSR_11P025KHZ_VALUE;
+		break;
+	case 12000:
+		aw88261->sr_value = AW88261_I2SSR_12KHZ_VALUE;
+		break;
+	case 16000:
+		aw88261->sr_value = AW88261_I2SSR_16KHZ_VALUE;
+		aw88261->cco_mux_value = AW88261_CCO_MUX_DIVIDED_VALUE;
+		break;
+	case 22050:
+		aw88261->sr_value = AW88261_I2SSR_22P05KHZ_VALUE;
+		break;
+	case 24000:
+		aw88261->sr_value = AW88261_I2SSR_24KHZ_VALUE;
+		break;
+	case 32000:
+		aw88261->sr_value = AW88261_I2SSR_32KHZ_VALUE;
+		aw88261->cco_mux_value = AW88261_CCO_MUX_DIVIDED_VALUE;
+		break;
+	case 44100:
+		aw88261->sr_value = AW88261_I2SSR_44P1KHZ_VALUE;
+		break;
+	case 48000:
+		aw88261->sr_value = AW88261_I2SSR_48KHZ_VALUE;
+		break;
+	case 96000:
+		aw88261->sr_value = AW88261_I2SSR_96KHZ_VALUE;
+		break;
+	case 192000:
+		aw88261->sr_value = AW88261_I2SSR_192KHZ_VALUE;
+		break;
+	default:
+		dev_err(aw88261->aw_pa->dev, "unsupported sample rate %d\n",
+			params_rate(params));
+		return -EINVAL;
+	}
+
+	switch (params_width(params)) {
+	case 16:
+		aw88261->fs_value = AW88261_I2SFS_16_BITS_VALUE;
+		break;
+	case 20:
+		aw88261->fs_value = AW88261_I2SFS_20_BITS_VALUE;
+		break;
+	case 24:
+		aw88261->fs_value = AW88261_I2SFS_24_BITS_VALUE;
+		break;
+	case 32:
+		aw88261->fs_value = AW88261_I2SFS_32_BITS_VALUE;
+		break;
+	default:
+		dev_err(aw88261->aw_pa->dev, "unsupported bit width %d\n",
+			params_width(params));
+		return -EINVAL;
+	}
+
+	switch (params_physical_width(params)) {
+	case 16:
+		aw88261->bck_value = AW88261_I2SBCK_32FS_VALUE;
+		break;
+	case 24:
+		aw88261->bck_value = AW88261_I2SBCK_48FS_VALUE;
+		break;
+	case 32:
+		aw88261->bck_value = AW88261_I2SBCK_64FS_VALUE;
+		break;
+	default:
+		dev_err(aw88261->aw_pa->dev, "unsupported physical bit width %d\n",
+			params_physical_width(params));
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops aw88261_dai_ops = {
+	.set_fmt = aw88261_set_fmt,
+	.hw_params = aw88261_hw_params,
+};
+
 static struct snd_soc_dai_driver aw88261_dai[] = {
 	{
 		.name = "aw88261-aif",
@@ -730,6 +842,7 @@ static struct snd_soc_dai_driver aw88261_dai[] = {
 			.rates = AW88261_RATES,
 			.formats = AW88261_FORMATS,
 		},
+		.ops = &aw88261_dai_ops,
 	},
 };
 
@@ -1249,6 +1362,14 @@ static int aw88261_i2c_probe(struct i2c_client *i2c)
 	if (!aw88261)
 		return -ENOMEM;
 
+	/* set defaults */
+	aw88261->sr_value = AW88261_I2SSR_48KHZ_VALUE;
+	aw88261->cco_mux_value = AW88261_CCO_MUX_BYPASS_VALUE;
+	aw88261->fs_value = AW88261_I2SFS_24_BITS_VALUE;
+	aw88261->bck_value = AW88261_I2SBCK_64FS_VALUE;
+	aw88261->bck_inv_value = AW88261_BCKINV_NOT_INVERT_VALUE;
+	aw88261->md_value = AW88261_I2SMD_PHILIPS_STANDARD_VALUE;
+
 	mutex_init(&aw88261->lock);
 
 	i2c_set_clientdata(i2c, aw88261);
diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h
index 1fee589608d6..3d7483d625a9 100644
--- a/sound/soc/codecs/aw88261.h
+++ b/sound/soc/codecs/aw88261.h
@@ -116,6 +116,19 @@
 #define AW88261_VCALK_SHIFT		(0)
 #define AW88261_VCALKL_SHIFT		(0)
 
+#define AW88261_BCKINV_START_BIT	(4)
+#define AW88261_BCKINV_BITS_LEN	(1)
+#define AW88261_BCKINV_MASK	\
+	(~(((1<<AW88261_BCKINV_BITS_LEN)-1) << AW88261_BCKINV_START_BIT))
+
+#define AW88261_BCKINV_NOT_INVERT	(0)
+#define AW88261_BCKINV_NOT_INVERT_VALUE	\
+	(AW88261_BCKINV_NOT_INVERT << AW88261_BCKINV_START_BIT)
+
+#define AW88261_BCKINV_INVERTED	(1)
+#define AW88261_BCKINV_INVERTED_VALUE	\
+	(AW88261_BCKINV_INVERTED << AW88261_BCKINV_START_BIT)
+
 #define AW88261_AMPPD_START_BIT	(1)
 #define AW88261_AMPPD_BITS_LEN		(1)
 #define AW88261_AMPPD_MASK		\
@@ -264,7 +277,98 @@
 #define AW88261_I2STXEN_ENABLE_VALUE	\
 	(AW88261_I2STXEN_ENABLE << AW88261_I2STXEN_START_BIT)
 
-#define AW88261_CCO_MUX_START_BIT	(14)
+#define AW88261_I2SMD_START_BIT	(8)
+#define AW88261_I2SMD_BITS_LEN	(2)
+#define AW88261_I2SMD_MASK	\
+	(~(((1<<AW88261_I2SMD_BITS_LEN)-1) << AW88261_I2SMD_START_BIT))
+
+#define AW88261_I2SMD_PHILIPS_STANDARD	(0)
+#define AW88261_I2SMD_PHILIPS_STANDARD_VALUE	\
+	(AW88261_I2SMD_PHILIPS_STANDARD << AW88261_I2SMD_START_BIT)
+
+#define AW88261_I2SMD_MSB_JUSTIFIED	(1)
+#define AW88261_I2SMD_MSB_JUSTIFIED_VALUE	\
+	(AW88261_I2SMD_MSB_JUSTIFIED << AW88261_I2SMD_START_BIT)
+
+#define AW88261_I2SMD_LSB_JUSTIFIED	(2)
+#define AW88261_I2SMD_LSB_JUSTIFIED_VALUE	\
+	(AW88261_I2SMD_LSB_JUSTIFIED << AW88261_I2SMD_START_BIT)
+
+#define AW88261_I2SFS_START_BIT			(6)
+#define AW88261_I2SFS_BITS_LEN			(2)
+#define AW88261_I2SFS_MASK				\
+	(~(((1<<AW88261_I2SFS_BITS_LEN)-1)<<AW88261_I2SFS_START_BIT))
+
+#define AW88261_I2SFS_16_BITS			(0)
+#define AW88261_I2SFS_16_BITS_VALUE		\
+	(AW88261_I2SFS_16_BITS << AW88261_I2SFS_START_BIT)
+#define AW88261_I2SFS_20_BITS			(1)
+#define AW88261_I2SFS_20_BITS_VALUE		\
+	(AW88261_I2SFS_20_BITS << AW88261_I2SFS_START_BIT)
+#define AW88261_I2SFS_24_BITS			(2)
+#define AW88261_I2SFS_24_BITS_VALUE		\
+	(AW88261_I2SFS_24_BITS << AW88261_I2SFS_START_BIT)
+#define AW88261_I2SFS_32_BITS			(3)
+#define AW88261_I2SFS_32_BITS_VALUE		\
+	(AW88261_I2SFS_32_BITS << AW88261_I2SFS_START_BIT)
+
+#define AW88261_I2SBCK_START_BIT	(4)
+#define AW88261_I2SBCK_BITS_LEN	(2)
+#define AW88261_I2SBCK_MASK	\
+	(~(((1<<AW88261_I2SBCK_BITS_LEN)-1) << AW88261_I2SBCK_START_BIT))
+
+#define AW88261_I2SBCK_32FS	(0)
+#define AW88261_I2SBCK_32FS_VALUE	\
+	(AW88261_I2SBCK_32FS << AW88261_I2SBCK_START_BIT)
+
+#define AW88261_I2SBCK_48FS	(1)
+#define AW88261_I2SBCK_48FS_VALUE	\
+	(AW88261_I2SBCK_48FS << AW88261_I2SBCK_START_BIT)
+
+#define AW88261_I2SBCK_64FS	(2)
+#define AW88261_I2SBCK_64FS_VALUE	\
+	(AW88261_I2SBCK_64FS << AW88261_I2SBCK_START_BIT)
+
+#define AW88261_I2SSR_START_BIT			(0)
+#define AW88261_I2SSR_BITS_LEN			(4)
+#define AW88261_I2SSR_MASK				\
+	(~(((1<<AW88261_I2SSR_BITS_LEN)-1) << AW88261_I2SSR_START_BIT))
+
+#define AW88261_I2SSR_8KHZ				(0)
+#define AW88261_I2SSR_8KHZ_VALUE		\
+	(AW88261_I2SSR_8KHZ << AW88261_I2SSR_START_BIT)
+#define AW88261_I2SSR_11P025KHZ			(1)
+#define AW88261_I2SSR_11P025KHZ_VALUE	\
+	(AW88261_I2SSR_11P025KHZ << AW88261_I2SSR_START_BIT)
+#define AW88261_I2SSR_12KHZ				(2)
+#define AW88261_I2SSR_12KHZ_VALUE		\
+	(AW88261_I2SSR_12KHZ << AW88261_I2SSR_START_BIT)
+#define AW88261_I2SSR_16KHZ				(3)
+#define AW88261_I2SSR_16KHZ_VALUE		\
+	(AW88261_I2SSR_16KHZ << AW88261_I2SSR_START_BIT)
+#define AW88261_I2SSR_22P05KHZ			(4)
+#define AW88261_I2SSR_22P05KHZ_VALUE	\
+	(AW88261_I2SSR_22P05KHZ << AW88261_I2SSR_START_BIT)
+#define AW88261_I2SSR_24KHZ				(5)
+#define AW88261_I2SSR_24KHZ_VALUE		\
+	(AW88261_I2SSR_24KHZ << AW88261_I2SSR_START_BIT)
+#define AW88261_I2SSR_32KHZ				(6)
+#define AW88261_I2SSR_32KHZ_VALUE		\
+	(AW88261_I2SSR_32KHZ << AW88261_I2SSR_START_BIT)
+#define AW88261_I2SSR_44P1KHZ			(7)
+#define AW88261_I2SSR_44P1KHZ_VALUE	\
+	(AW88261_I2SSR_44P1KHZ << AW88261_I2SSR_START_BIT)
+#define AW88261_I2SSR_48KHZ				(8)
+#define AW88261_I2SSR_48KHZ_VALUE		\
+	(AW88261_I2SSR_48KHZ << AW88261_I2SSR_START_BIT)
+#define AW88261_I2SSR_96KHZ				(9)
+#define AW88261_I2SSR_96KHZ_VALUE		\
+	(AW88261_I2SSR_96KHZ << AW88261_I2SSR_START_BIT)
+#define AW88261_I2SSR_192KHZ			(10)
+#define AW88261_I2SSR_192KHZ_VALUE		\
+	(AW88261_I2SSR_192KHZ << AW88261_I2SSR_START_BIT)
+
+#define AW88261_CCO_MUX_START_BIT	(6)
 #define AW88261_CCO_MUX_BITS_LEN	(1)
 #define AW88261_CCO_MUX_MASK		\
 	(~(((1<<AW88261_CCO_MUX_BITS_LEN)-1) << AW88261_CCO_MUX_START_BIT))
@@ -370,7 +474,10 @@
 #define AW88261_START_RETRIES		(5)
 #define AW88261_START_WORK_DELAY_MS	(0)
 
+/* NOTE: 192000 has a reg value donwstream but not listed in datasheet */
 #define AW88261_RATES (SNDRV_PCM_RATE_8000_48000 | \
+			SNDRV_PCM_RATE_12000 | \
+			SNDRV_PCM_RATE_24000 | \
 			SNDRV_PCM_RATE_96000)
 #define AW88261_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
 			SNDRV_PCM_FMTBIT_S24_LE | \
@@ -451,6 +558,13 @@ struct aw88261 {
 	unsigned int mute_st;
 	unsigned int amppd_st;
 
+	unsigned int sr_value;
+	unsigned int cco_mux_value;
+	unsigned int fs_value;
+	unsigned int bck_value;
+	unsigned int bck_inv_value;
+	unsigned int md_value;
+
 	bool phase_sync;
 };
 
-- 
2.53.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v3 2/7] ASoC: codecs: aw88261: add TDM support
  2026-05-29 20:05 [PATCH v3 0/7] ASoC: codecs: aw88261: fixes and cleanup Val Packett
  2026-05-29 20:05 ` [PATCH v3 1/7] ASoC: codecs: aw88261: support changing sample rate and bit width Val Packett
@ 2026-05-29 20:05 ` Val Packett
  2026-05-29 20:05 ` [PATCH v3 3/7] ASoC: codecs: aw88261: reduce log spam Val Packett
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Val Packett @ 2026-05-29 20:05 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai
  Cc: Val Packett, Luca Weiss, ~postmarketos/upstreaming, phone-devel,
	linux-sound, linux-kernel

This amp supports TDM mode, so implement the set_tdm_slot operation to
let the SoC driver configure the TDM slot number, width, and masks.

Signed-off-by: Val Packett <val@packett.cool>
---
 sound/soc/codecs/aw88261.c | 115 ++++++++++++++++++++++++++++++++++++-
 sound/soc/codecs/aw88261.h |  56 ++++++++++++++++++
 2 files changed, 170 insertions(+), 1 deletion(-)

diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c
index 6d4b911be298..d6a6173d8af4 100644
--- a/sound/soc/codecs/aw88261.c
+++ b/sound/soc/codecs/aw88261.c
@@ -10,6 +10,7 @@
 
 #include <linux/i2c.h>
 #include <linux/firmware.h>
+#include <linux/bitops.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <sound/soc.h>
@@ -181,6 +182,30 @@ static int aw88261_dev_configure_syspll(struct aw88261 *aw88261)
 	struct aw_device *aw_dev = aw88261->aw_pa;
 	int ret;
 
+	/* Configure TDM slots (I2S is represented as no slots) */
+	ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL2_REG,
+			~AW88261_SLOT_NUM_MASK, aw88261->slot_num_value);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL2_REG,
+			~AW88261_I2S_TX_SLOTVLD_MASK,
+			aw88261->tx_slotvld_mask);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL2_REG,
+			~AW88261_I2S_RXL_SLOTVLD_MASK,
+			aw88261->rxl_slotvld_mask);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL2_REG,
+			~AW88261_I2S_RXR_SLOTVLD_MASK,
+			aw88261->rxr_slotvld_mask);
+	if (ret)
+		return ret;
+
 	/* PLL divider must be used for 8/16/32 kHz modes */
 	ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG,
 			~AW88261_CCO_MUX_MASK, aw88261->cco_mux_value);
@@ -195,7 +220,9 @@ static int aw88261_dev_configure_syspll(struct aw88261 *aw88261)
 
 	/* The bit clock (BCK) defines the length of a frame */
 	ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG,
-			~AW88261_I2SBCK_MASK, aw88261->bck_value);
+			~AW88261_I2SBCK_MASK,
+			(aw88261->tdm_bck_value != AW88261_TDM_BCK_UNSET)
+			? aw88261->tdm_bck_value : aw88261->bck_value);
 	if (ret)
 		return ret;
 
@@ -710,9 +737,11 @@ static int aw88261_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 
 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 	case SND_SOC_DAIFMT_I2S:
+	case SND_SOC_DAIFMT_DSP_A:
 		aw88261->md_value = AW88261_I2SMD_PHILIPS_STANDARD_VALUE;
 		break;
 	case SND_SOC_DAIFMT_MSB:
+	case SND_SOC_DAIFMT_DSP_B:
 		aw88261->md_value = AW88261_I2SMD_MSB_JUSTIFIED_VALUE;
 		break;
 	case SND_SOC_DAIFMT_LSB:
@@ -819,9 +848,90 @@ static int aw88261_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
+static int aw88261_set_tdm_slot(struct snd_soc_dai *dai,
+	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+	struct snd_soc_component *component = dai->component;
+	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+	int chan;
+
+	switch (slots) {
+	case 0:
+		/* Just reset everything TDM related to I2S values */
+		aw88261->slot_num_value = AW88261_SLOT_NUM_I2S_MODE_VALUE;
+		aw88261->tdm_bck_value = AW88261_TDM_BCK_UNSET;
+		aw88261->tx_slotvld_mask = 0 << AW88261_I2S_TX_SLOTVLD_START_BIT;
+		aw88261->rxl_slotvld_mask = 0 << AW88261_I2S_RXL_SLOTVLD_START_BIT;
+		aw88261->rxr_slotvld_mask = 1 << AW88261_I2S_RXR_SLOTVLD_START_BIT;
+		return 0;
+	case 1:
+		aw88261->slot_num_value = AW88261_SLOT_NUM_TDM1S_VALUE;
+		break;
+	case 2:
+		aw88261->slot_num_value = AW88261_SLOT_NUM_TDM2S_VALUE;
+		break;
+	case 4:
+		aw88261->slot_num_value = AW88261_SLOT_NUM_TDM4S_VALUE;
+		break;
+	case 6:
+		aw88261->slot_num_value = AW88261_SLOT_NUM_TDM6S_VALUE;
+		break;
+	case 8:
+		aw88261->slot_num_value = AW88261_SLOT_NUM_TDM8S_VALUE;
+		break;
+	case 16:
+		aw88261->slot_num_value = AW88261_SLOT_NUM_TDM16S_VALUE;
+		break;
+	default:
+		dev_err(aw88261->aw_pa->dev, "unsupported slot count %d\n", slots);
+		return -EINVAL;
+	}
+
+	switch (slot_width) {
+	case 16:
+		aw88261->tdm_bck_value = AW88261_I2SBCK_32FS_VALUE;
+		break;
+	case 20:
+	case 24:
+		aw88261->tdm_bck_value = AW88261_I2SBCK_48FS_VALUE;
+		break;
+	case 32:
+		aw88261->tdm_bck_value = AW88261_I2SBCK_64FS_VALUE;
+		break;
+	default:
+		dev_err(aw88261->aw_pa->dev, "unsupported slot width %d\n",
+			slot_width);
+		return -EINVAL;
+	}
+
+	if (tx_mask != 0) {
+		if ((chan = __ffs(tx_mask)) > 16)
+			return -EINVAL;
+
+		aw88261->tx_slotvld_mask = chan << AW88261_I2S_TX_SLOTVLD_START_BIT;
+	}
+
+	if (rx_mask != 0) {
+		if ((chan = __ffs(rx_mask)) > 16)
+			return -EINVAL;
+
+		aw88261->rxl_slotvld_mask = chan << AW88261_I2S_RXL_SLOTVLD_START_BIT;
+	}
+
+	if ((rx_mask & ~BIT(chan)) != 0) {
+		if ((chan = __ffs(rx_mask & ~BIT(chan))) > 16)
+			return -EINVAL;
+
+		aw88261->rxr_slotvld_mask = chan << AW88261_I2S_RXR_SLOTVLD_START_BIT;
+	}
+
+	return 0;
+}
+
 static const struct snd_soc_dai_ops aw88261_dai_ops = {
 	.set_fmt = aw88261_set_fmt,
 	.hw_params = aw88261_hw_params,
+	.set_tdm_slot = aw88261_set_tdm_slot,
 };
 
 static struct snd_soc_dai_driver aw88261_dai[] = {
@@ -1363,12 +1473,15 @@ static int aw88261_i2c_probe(struct i2c_client *i2c)
 		return -ENOMEM;
 
 	/* set defaults */
+	aw88261->slot_num_value = AW88261_SLOT_NUM_I2S_MODE_VALUE;
 	aw88261->sr_value = AW88261_I2SSR_48KHZ_VALUE;
 	aw88261->cco_mux_value = AW88261_CCO_MUX_BYPASS_VALUE;
 	aw88261->fs_value = AW88261_I2SFS_24_BITS_VALUE;
 	aw88261->bck_value = AW88261_I2SBCK_64FS_VALUE;
 	aw88261->bck_inv_value = AW88261_BCKINV_NOT_INVERT_VALUE;
+	aw88261->tdm_bck_value = AW88261_TDM_BCK_UNSET;
 	aw88261->md_value = AW88261_I2SMD_PHILIPS_STANDARD_VALUE;
+	aw88261->rxr_slotvld_mask = 1 << AW88261_I2S_RXR_SLOTVLD_START_BIT;
 
 	mutex_init(&aw88261->lock);
 
diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h
index 3d7483d625a9..d75f55cdca42 100644
--- a/sound/soc/codecs/aw88261.h
+++ b/sound/soc/codecs/aw88261.h
@@ -329,6 +329,8 @@
 #define AW88261_I2SBCK_64FS_VALUE	\
 	(AW88261_I2SBCK_64FS << AW88261_I2SBCK_START_BIT)
 
+#define AW88261_TDM_BCK_UNSET			UINT_MAX
+
 #define AW88261_I2SSR_START_BIT			(0)
 #define AW88261_I2SSR_BITS_LEN			(4)
 #define AW88261_I2SSR_MASK				\
@@ -368,6 +370,54 @@
 #define AW88261_I2SSR_192KHZ_VALUE		\
 	(AW88261_I2SSR_192KHZ << AW88261_I2SSR_START_BIT)
 
+#define AW88261_SLOT_NUM_START_BIT	(12)
+#define AW88261_SLOT_NUM_BITS_LEN	(3)
+#define AW88261_SLOT_NUM_MASK	\
+	(~(((1<<AW88261_SLOT_NUM_BITS_LEN)-1) << AW88261_SLOT_NUM_START_BIT))
+
+#define AW88261_SLOT_NUM_I2S_MODE	(0)
+#define AW88261_SLOT_NUM_I2S_MODE_VALUE	\
+	(AW88261_SLOT_NUM_I2S_MODE << AW88261_SLOT_NUM_START_BIT)
+
+#define AW88261_SLOT_NUM_TDM1S	(1)
+#define AW88261_SLOT_NUM_TDM1S_VALUE	\
+	(AW88261_SLOT_NUM_TDM1S << AW88261_SLOT_NUM_START_BIT)
+
+#define AW88261_SLOT_NUM_TDM2S	(2)
+#define AW88261_SLOT_NUM_TDM2S_VALUE	\
+	(AW88261_SLOT_NUM_TDM2S << AW88261_SLOT_NUM_START_BIT)
+
+#define AW88261_SLOT_NUM_TDM4S	(3)
+#define AW88261_SLOT_NUM_TDM4S_VALUE	\
+	(AW88261_SLOT_NUM_TDM4S << AW88261_SLOT_NUM_START_BIT)
+
+#define AW88261_SLOT_NUM_TDM6S	(4)
+#define AW88261_SLOT_NUM_TDM6S_VALUE	\
+	(AW88261_SLOT_NUM_TDM6S << AW88261_SLOT_NUM_START_BIT)
+
+#define AW88261_SLOT_NUM_TDM8S	(5)
+#define AW88261_SLOT_NUM_TDM8S_VALUE	\
+	(AW88261_SLOT_NUM_TDM8S << AW88261_SLOT_NUM_START_BIT)
+
+#define AW88261_SLOT_NUM_TDM16S	(6)
+#define AW88261_SLOT_NUM_TDM16S_VALUE	\
+	(AW88261_SLOT_NUM_TDM16S << AW88261_SLOT_NUM_START_BIT)
+
+#define AW88261_I2S_TX_SLOTVLD_START_BIT	(8)
+#define AW88261_I2S_TX_SLOTVLD_BITS_LEN	(4)
+#define AW88261_I2S_TX_SLOTVLD_MASK	\
+	(~(((1<<AW88261_I2S_TX_SLOTVLD_BITS_LEN)-1) << AW88261_I2S_TX_SLOTVLD_START_BIT))
+
+#define AW88261_I2S_RXR_SLOTVLD_START_BIT	(4)
+#define AW88261_I2S_RXR_SLOTVLD_BITS_LEN	(4)
+#define AW88261_I2S_RXR_SLOTVLD_MASK	\
+	(~(((1<<AW88261_I2S_RXR_SLOTVLD_BITS_LEN)-1) << AW88261_I2S_RXR_SLOTVLD_START_BIT))
+
+#define AW88261_I2S_RXL_SLOTVLD_START_BIT	(0)
+#define AW88261_I2S_RXL_SLOTVLD_BITS_LEN	(4)
+#define AW88261_I2S_RXL_SLOTVLD_MASK	\
+	(~(((1<<AW88261_I2S_RXL_SLOTVLD_BITS_LEN)-1) << AW88261_I2S_RXL_SLOTVLD_START_BIT))
+
 #define AW88261_CCO_MUX_START_BIT	(6)
 #define AW88261_CCO_MUX_BITS_LEN	(1)
 #define AW88261_CCO_MUX_MASK		\
@@ -563,8 +613,14 @@ struct aw88261 {
 	unsigned int fs_value;
 	unsigned int bck_value;
 	unsigned int bck_inv_value;
+	unsigned int tdm_bck_value;
 	unsigned int md_value;
 
+	unsigned int slot_num_value;
+	unsigned int tx_slotvld_mask;
+	unsigned int rxl_slotvld_mask;
+	unsigned int rxr_slotvld_mask;
+
 	bool phase_sync;
 };
 
-- 
2.53.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v3 3/7] ASoC: codecs: aw88261: reduce log spam
  2026-05-29 20:05 [PATCH v3 0/7] ASoC: codecs: aw88261: fixes and cleanup Val Packett
  2026-05-29 20:05 ` [PATCH v3 1/7] ASoC: codecs: aw88261: support changing sample rate and bit width Val Packett
  2026-05-29 20:05 ` [PATCH v3 2/7] ASoC: codecs: aw88261: add TDM support Val Packett
@ 2026-05-29 20:05 ` Val Packett
  2026-05-29 20:05 ` [PATCH v3 4/7] ASoC: codecs: aw88261: remove fade in/out on start/stop Val Packett
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Val Packett @ 2026-05-29 20:05 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai
  Cc: Val Packett, Luca Weiss, ~postmarketos/upstreaming, phone-devel,
	linux-sound, linux-kernel

This driver would create a wall of logspam during initialization due to
e.g. the PLL not being ready while waiting for it to stabilize. Change
intermediate dev_err() calls to dev_dbg() to reduce the noise.

While here, log the detected chip ID when that check fails.

Signed-off-by: Val Packett <val@packett.cool>
---
 sound/soc/codecs/aw88261.c | 24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)

diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c
index d6a6173d8af4..a20b66239d50 100644
--- a/sound/soc/codecs/aw88261.c
+++ b/sound/soc/codecs/aw88261.c
@@ -153,7 +153,7 @@ static int aw88261_dev_get_iis_status(struct aw_device *aw_dev)
 	if (ret)
 		return ret;
 	if ((reg_val & AW88261_BIT_PLL_CHECK) != AW88261_BIT_PLL_CHECK) {
-		dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val);
+		dev_dbg(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val);
 		return -EINVAL;
 	}
 
@@ -167,7 +167,7 @@ static int aw88261_dev_check_pll(struct aw_device *aw_dev)
 	for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
 		ret = aw88261_dev_get_iis_status(aw_dev);
 		if (ret) {
-			dev_err(aw_dev->dev, "mode1 iis signal check error");
+			dev_dbg(aw_dev->dev, "mode1 iis signal check error");
 			usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
 		} else {
 			return ret;
@@ -261,7 +261,7 @@ static int aw88261_dev_check_sysst(struct aw_device *aw_dev)
 		check_val = reg_val & (~AW88261_BIT_SYSST_CHECK_MASK)
 							& AW88261_BIT_SYSST_CHECK;
 		if (check_val != AW88261_BIT_SYSST_CHECK) {
-			dev_err(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x",
+			dev_dbg(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x",
 				reg_val, AW88261_BIT_SYSST_CHECK);
 			usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
 		} else {
@@ -555,7 +555,7 @@ static int aw88261_dev_start(struct aw88261 *aw88261)
 	int ret;
 
 	if (aw_dev->status == AW88261_DEV_PW_ON) {
-		dev_info(aw_dev->dev, "already power on");
+		dev_dbg(aw_dev->dev, "already power on");
 		return 0;
 	}
 
@@ -565,7 +565,7 @@ static int aw88261_dev_start(struct aw88261 *aw88261)
 
 	ret = aw88261_dev_configure_syspll(aw88261);
 	if (ret) {
-		dev_err(aw_dev->dev, "pll check failed cannot start");
+		dev_dbg(aw_dev->dev, "pll check failed");
 		goto pll_check_fail;
 	}
 
@@ -576,7 +576,7 @@ static int aw88261_dev_start(struct aw88261 *aw88261)
 	/* check i2s status */
 	ret = aw88261_dev_check_sysst(aw_dev);
 	if (ret) {
-		dev_err(aw_dev->dev, "sysst check failed");
+		dev_dbg(aw_dev->dev, "sysst check failed");
 		goto sysst_check_fail;
 	}
 
@@ -677,18 +677,22 @@ static void aw88261_start_pa(struct aw88261 *aw88261)
 	for (i = 0; i < AW88261_START_RETRIES; i++) {
 		ret = aw88261_reg_update(aw88261, aw88261->phase_sync);
 		if (ret) {
-			dev_err(aw88261->aw_pa->dev, "fw update failed, cnt:%d\n", i);
+			dev_dbg(aw88261->aw_pa->dev,
+				"aw88261_reg_update failed, cnt:%d, ret:%d\n", i, ret);
 			continue;
 		}
 		ret = aw88261_dev_start(aw88261);
 		if (ret) {
-			dev_err(aw88261->aw_pa->dev, "aw88261 device start failed. retry = %d", i);
+			dev_dbg(aw88261->aw_pa->dev,
+				"aw88261_dev_start failed, cnt:%d, ret:%d\n", i, ret);
 			continue;
 		} else {
-			dev_info(aw88261->aw_pa->dev, "start success\n");
+			dev_dbg(aw88261->aw_pa->dev, "start success\n");
 			break;
 		}
 	}
+	if (ret != 0)
+		dev_err(aw88261->aw_pa->dev, "start failure (%d)\n", ret);
 }
 
 static void aw88261_startup_work(struct work_struct *work)
@@ -1431,7 +1435,7 @@ static int aw88261_init(struct aw88261 *aw88261, struct i2c_client *i2c, struct
 		return ret;
 	}
 	if (chip_id != AW88261_CHIP_ID) {
-		dev_err(&i2c->dev, "unsupported device");
+		dev_err(&i2c->dev, "unsupported device id = %x", chip_id);
 		return -ENXIO;
 	}
 
-- 
2.53.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v3 4/7] ASoC: codecs: aw88261: remove fade in/out on start/stop
  2026-05-29 20:05 [PATCH v3 0/7] ASoC: codecs: aw88261: fixes and cleanup Val Packett
                   ` (2 preceding siblings ...)
  2026-05-29 20:05 ` [PATCH v3 3/7] ASoC: codecs: aw88261: reduce log spam Val Packett
@ 2026-05-29 20:05 ` Val Packett
  2026-05-29 20:05 ` [PATCH v3 5/7] ASoC: codecs: aw88261: remove async start Val Packett
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Val Packett @ 2026-05-29 20:05 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai
  Cc: Val Packett, Luca Weiss, ~postmarketos/upstreaming, phone-devel,
	linux-sound, linux-kernel

This "feature" was copied from downstream, but it does not belong in
the kernel at all. Remove it to simplify the driver.

Signed-off-by: Val Packett <val@packett.cool>
---
 sound/soc/codecs/aw88261.c | 158 +------------------------------------
 sound/soc/codecs/aw88261.h |   3 -
 2 files changed, 2 insertions(+), 159 deletions(-)

diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c
index a20b66239d50..f6a93244046d 100644
--- a/sound/soc/codecs/aw88261.c
+++ b/sound/soc/codecs/aw88261.c
@@ -45,50 +45,6 @@ static void aw88261_dev_set_volume(struct aw_device *aw_dev, unsigned int value)
 	regmap_write(aw_dev->regmap, AW88261_SYSCTRL2_REG, real_value);
 }
 
-static void aw88261_dev_fade_in(struct aw_device *aw_dev)
-{
-	struct aw_volume_desc *desc = &aw_dev->volume_desc;
-	int fade_in_vol = desc->ctl_volume;
-	int fade_step = aw_dev->fade_step;
-	int i;
-
-	if (fade_step == 0 || aw_dev->fade_in_time == 0) {
-		aw88261_dev_set_volume(aw_dev, fade_in_vol);
-		return;
-	}
-
-	for (i = AW88261_MUTE_VOL; i >= fade_in_vol; i -= fade_step) {
-		aw88261_dev_set_volume(aw_dev, i);
-		usleep_range(aw_dev->fade_in_time,
-					aw_dev->fade_in_time + 10);
-	}
-
-	if (i != fade_in_vol)
-		aw88261_dev_set_volume(aw_dev, fade_in_vol);
-}
-
-static void aw88261_dev_fade_out(struct aw_device *aw_dev)
-{
-	struct aw_volume_desc *desc = &aw_dev->volume_desc;
-	int fade_step = aw_dev->fade_step;
-	int i;
-
-	if (fade_step == 0 || aw_dev->fade_out_time == 0) {
-		aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL);
-		return;
-	}
-
-	for (i = desc->ctl_volume; i <= AW88261_MUTE_VOL; i += fade_step) {
-		aw88261_dev_set_volume(aw_dev, i);
-		usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
-	}
-
-	if (i != AW88261_MUTE_VOL) {
-		aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL);
-		usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
-	}
-}
-
 static void aw88261_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag)
 {
 	if (flag)
@@ -122,13 +78,13 @@ static void aw88261_dev_amppd(struct aw_device *aw_dev, bool amppd)
 static void aw88261_dev_mute(struct aw_device *aw_dev, bool is_mute)
 {
 	if (is_mute) {
-		aw88261_dev_fade_out(aw_dev);
+		aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL);
 		regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
 				~AW88261_HMUTE_MASK, AW88261_HMUTE_ENABLE_VALUE);
 	} else {
 		regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
 				~AW88261_HMUTE_MASK, AW88261_HMUTE_DISABLE_VALUE);
-		aw88261_dev_fade_in(aw_dev);
+		aw88261_dev_set_volume(aw_dev, aw_dev->volume_desc.ctl_volume);
 	}
 }
 
@@ -960,75 +916,6 @@ static struct snd_soc_dai_driver aw88261_dai[] = {
 	},
 };
 
-static int aw88261_get_fade_in_time(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
-	struct aw_device *aw_dev = aw88261->aw_pa;
-
-	ucontrol->value.integer.value[0] = aw_dev->fade_in_time;
-
-	return 0;
-}
-
-static int aw88261_set_fade_in_time(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
-	struct soc_mixer_control *mc =
-		(struct soc_mixer_control *)kcontrol->private_value;
-	struct aw_device *aw_dev = aw88261->aw_pa;
-	int time;
-
-	time = ucontrol->value.integer.value[0];
-
-	if (time < mc->min || time > mc->max)
-		return -EINVAL;
-
-	if (time != aw_dev->fade_in_time) {
-		aw_dev->fade_in_time = time;
-		return 1;
-	}
-
-	return 0;
-}
-
-static int aw88261_get_fade_out_time(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
-	struct aw_device *aw_dev = aw88261->aw_pa;
-
-	ucontrol->value.integer.value[0] = aw_dev->fade_out_time;
-
-	return 0;
-}
-
-static int aw88261_set_fade_out_time(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
-	struct soc_mixer_control *mc =
-		(struct soc_mixer_control *)kcontrol->private_value;
-	struct aw_device *aw_dev = aw88261->aw_pa;
-	int time;
-
-	time = ucontrol->value.integer.value[0];
-	if (time < mc->min || time > mc->max)
-		return -EINVAL;
-
-	if (time != aw_dev->fade_out_time) {
-		aw_dev->fade_out_time = time;
-		return 1;
-	}
-
-	return 0;
-}
-
 static int aw88261_dev_set_profile_index(struct aw_device *aw_dev, int index)
 {
 	/* check the index whether is valid */
@@ -1152,48 +1039,10 @@ static int aw88261_volume_set(struct snd_kcontrol *kcontrol,
 	return 0;
 }
 
-static int aw88261_get_fade_step(struct snd_kcontrol *kcontrol,
-				struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
-	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
-
-	ucontrol->value.integer.value[0] = aw88261->aw_pa->fade_step;
-
-	return 0;
-}
-
-static int aw88261_set_fade_step(struct snd_kcontrol *kcontrol,
-				struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
-	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
-	struct soc_mixer_control *mc =
-		(struct soc_mixer_control *)kcontrol->private_value;
-	int value;
-
-	value = ucontrol->value.integer.value[0];
-	if (value < mc->min || value > mc->max)
-		return -EINVAL;
-
-	if (aw88261->aw_pa->fade_step != value) {
-		aw88261->aw_pa->fade_step = value;
-		return 1;
-	}
-
-	return 0;
-}
-
 static const struct snd_kcontrol_new aw88261_controls[] = {
 	SOC_SINGLE_EXT("PCM Playback Volume", AW88261_SYSCTRL2_REG,
 		6, AW88261_MUTE_VOL, 0, aw88261_volume_get,
 		aw88261_volume_set),
-	SOC_SINGLE_EXT("Fade Step", 0, 0, AW88261_MUTE_VOL, 0,
-		aw88261_get_fade_step, aw88261_set_fade_step),
-	SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
-		aw88261_get_fade_in_time, aw88261_set_fade_in_time),
-	SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
-		aw88261_get_fade_out_time, aw88261_set_fade_out_time),
 	AW88261_PROFILE_EXT("Profile Set", aw88261_profile_info,
 		aw88261_profile_get, aw88261_profile_set),
 };
@@ -1284,8 +1133,6 @@ static int aw88261_dev_init(struct aw88261 *aw88261, struct aw_container *aw_cfg
 	if (ret)
 		return ret;
 
-	aw_dev->fade_in_time = AW88261_500_US;
-	aw_dev->fade_out_time = AW88261_500_US;
 	aw_dev->prof_cur = AW88261_INIT_PROFILE;
 	aw_dev->prof_index = AW88261_INIT_PROFILE;
 
@@ -1456,7 +1303,6 @@ static int aw88261_init(struct aw88261 *aw88261, struct i2c_client *i2c, struct
 	aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID;
 	aw_dev->channel = 0;
 	aw_dev->fw_status = AW88261_DEV_FW_FAILED;
-	aw_dev->fade_step = AW88261_VOLUME_STEP_DB;
 	aw_dev->volume_desc.ctl_volume = AW88261_VOL_DEFAULT_VALUE;
 	aw_dev->volume_desc.mute_volume = AW88261_MUTE_VOL;
 	aw88261_parse_channel_dt(aw88261);
diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h
index d75f55cdca42..9a5384624541 100644
--- a/sound/soc/codecs/aw88261.h
+++ b/sound/soc/codecs/aw88261.h
@@ -533,9 +533,6 @@
 			SNDRV_PCM_FMTBIT_S24_LE | \
 			SNDRV_PCM_FMTBIT_S32_LE)
 
-#define FADE_TIME_MAX			100000
-#define FADE_TIME_MIN			0
-
 #define AW88261_DEV_DEFAULT_CH		(0)
 #define AW88261_ACF_FILE		"aw88261_acf.bin"
 #define AW88261_DEV_SYSST_CHECK_MAX	(10)
-- 
2.53.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v3 5/7] ASoC: codecs: aw88261: remove async start
  2026-05-29 20:05 [PATCH v3 0/7] ASoC: codecs: aw88261: fixes and cleanup Val Packett
                   ` (3 preceding siblings ...)
  2026-05-29 20:05 ` [PATCH v3 4/7] ASoC: codecs: aw88261: remove fade in/out on start/stop Val Packett
@ 2026-05-29 20:05 ` Val Packett
  2026-05-29 20:05 ` [PATCH v3 6/7] ASoC: codecs: aw88261: fix incorrect masks for boost regs Val Packett
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Val Packett @ 2026-05-29 20:05 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai
  Cc: Val Packett, Luca Weiss, ~postmarketos/upstreaming, phone-devel,
	linux-sound, linux-kernel

Codec drivers are not supposed to do anything like this. The result was
that the first second or so of playback was essentially inaudible, and
very short alert sounds could be missed entirely. Let's not do this.

Signed-off-by: Val Packett <val@packett.cool>
---
 sound/soc/codecs/aw88261.c | 33 ++++-----------------------------
 sound/soc/codecs/aw88261.h |  6 ------
 2 files changed, 4 insertions(+), 35 deletions(-)

diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c
index f6a93244046d..fca62dcfe15e 100644
--- a/sound/soc/codecs/aw88261.c
+++ b/sound/soc/codecs/aw88261.c
@@ -651,17 +651,7 @@ static void aw88261_start_pa(struct aw88261 *aw88261)
 		dev_err(aw88261->aw_pa->dev, "start failure (%d)\n", ret);
 }
 
-static void aw88261_startup_work(struct work_struct *work)
-{
-	struct aw88261 *aw88261 =
-		container_of(work, struct aw88261, start_work.work);
-
-	mutex_lock(&aw88261->lock);
-	aw88261_start_pa(aw88261);
-	mutex_unlock(&aw88261->lock);
-}
-
-static void aw88261_start(struct aw88261 *aw88261, bool sync_start)
+static void aw88261_start(struct aw88261 *aw88261)
 {
 	if (aw88261->aw_pa->fw_status != AW88261_DEV_FW_OK)
 		return;
@@ -669,12 +659,7 @@ static void aw88261_start(struct aw88261 *aw88261, bool sync_start)
 	if (aw88261->aw_pa->status == AW88261_DEV_PW_ON)
 		return;
 
-	if (sync_start == AW88261_SYNC_START)
-		aw88261_start_pa(aw88261);
-	else
-		queue_delayed_work(system_dfl_wq,
-			&aw88261->start_work,
-			AW88261_START_WORK_DELAY_MS);
+	aw88261_start_pa(aw88261);
 }
 
 static int aw88261_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
@@ -994,7 +979,7 @@ static int aw88261_profile_set(struct snd_kcontrol *kcontrol,
 
 	if (aw88261->aw_pa->status) {
 		aw88261_dev_stop(aw88261->aw_pa);
-		aw88261_start(aw88261, AW88261_SYNC_START);
+		aw88261_start(aw88261);
 	}
 
 	mutex_unlock(&aw88261->lock);
@@ -1056,7 +1041,7 @@ static int aw88261_playback_event(struct snd_soc_dapm_widget *w,
 	mutex_lock(&aw88261->lock);
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-		aw88261_start(aw88261, AW88261_ASYNC_START);
+		aw88261_start(aw88261);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
 		aw88261_dev_stop(aw88261->aw_pa);
@@ -1216,8 +1201,6 @@ static int aw88261_codec_probe(struct snd_soc_component *component)
 	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
 	int ret;
 
-	INIT_DELAYED_WORK(&aw88261->start_work, aw88261_startup_work);
-
 	ret = aw88261_request_firmware_file(aw88261);
 	if (ret)
 		return dev_err_probe(aw88261->aw_pa->dev, ret,
@@ -1241,16 +1224,8 @@ static int aw88261_codec_probe(struct snd_soc_component *component)
 	return ret;
 }
 
-static void aw88261_codec_remove(struct snd_soc_component *aw_codec)
-{
-	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(aw_codec);
-
-	cancel_delayed_work_sync(&aw88261->start_work);
-}
-
 static const struct snd_soc_component_driver soc_codec_dev_aw88261 = {
 	.probe = aw88261_codec_probe,
-	.remove = aw88261_codec_remove,
 };
 
 static void aw88261_parse_channel_dt(struct aw88261 *aw88261)
diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h
index 9a5384624541..4be6ac02123e 100644
--- a/sound/soc/codecs/aw88261.h
+++ b/sound/soc/codecs/aw88261.h
@@ -557,11 +557,6 @@
 	.put = profile_set, \
 }
 
-enum {
-	AW88261_SYNC_START = 0,
-	AW88261_ASYNC_START,
-};
-
 enum aw88261_id {
 	AW88261_CHIP_ID = 0x2113,
 };
@@ -596,7 +591,6 @@ struct aw88261 {
 	struct aw_device *aw_pa;
 	struct mutex lock;
 	struct gpio_desc *reset_gpio;
-	struct delayed_work start_work;
 	struct regmap *regmap;
 	struct aw_container *aw_cfg;
 
-- 
2.53.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v3 6/7] ASoC: codecs: aw88261: fix incorrect masks for boost regs
  2026-05-29 20:05 [PATCH v3 0/7] ASoC: codecs: aw88261: fixes and cleanup Val Packett
                   ` (4 preceding siblings ...)
  2026-05-29 20:05 ` [PATCH v3 5/7] ASoC: codecs: aw88261: remove async start Val Packett
@ 2026-05-29 20:05 ` Val Packett
  2026-05-29 20:05 ` [PATCH v3 7/7] ASoC: codecs: aw88261: make volume control usable Val Packett
  2026-06-09 23:09 ` [PATCH v3 0/7] ASoC: codecs: aw88261: fixes and cleanup Mark Brown
  7 siblings, 0 replies; 9+ messages in thread
From: Val Packett @ 2026-05-29 20:05 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai, Weidong Wang
  Cc: Val Packett, Luca Weiss, ~postmarketos/upstreaming, phone-devel,
	linux-sound, linux-kernel

The boost-related register fields used in aw88261_reg_force_set use the
exact same definitions as the rest of the fields, where the mask must be
inverted when passing it to regmap_update_bits, but they weren't
inverted here.

Fixes: 028a2ae25691 ("ASoC: codecs: Add aw88261 amplifier driver")
Signed-off-by: Val Packett <val@packett.cool>
---
 sound/soc/codecs/aw88261.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c
index fca62dcfe15e..01e93bc018b9 100644
--- a/sound/soc/codecs/aw88261.c
+++ b/sound/soc/codecs/aw88261.c
@@ -245,22 +245,22 @@ static void aw88261_reg_force_set(struct aw88261 *aw88261)
 	if (aw88261->frcset_en == AW88261_FRCSET_ENABLE) {
 		/* set FORCE_PWM */
 		regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL3_REG,
-				AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE);
+				~AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE);
 		/* set BOOST_OS_WIDTH */
 		regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL5_REG,
-				AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE);
+				~AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE);
 		/* set BURST_LOOPR */
 		regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL6_REG,
-				AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE);
+				~AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE);
 		/* set RSQN_DLY */
 		regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL7_REG,
-				AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE);
+				~AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE);
 		/* set BURST_SSMODE */
 		regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL8_REG,
-				AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE);
+				~AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE);
 		/* set BST_BURST */
 		regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL9_REG,
-				AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE);
+				~AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE);
 	} else {
 		dev_dbg(aw88261->aw_pa->dev, "needn't set reg value");
 	}
-- 
2.53.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v3 7/7] ASoC: codecs: aw88261: make volume control usable
  2026-05-29 20:05 [PATCH v3 0/7] ASoC: codecs: aw88261: fixes and cleanup Val Packett
                   ` (5 preceding siblings ...)
  2026-05-29 20:05 ` [PATCH v3 6/7] ASoC: codecs: aw88261: fix incorrect masks for boost regs Val Packett
@ 2026-05-29 20:05 ` Val Packett
  2026-06-09 23:09 ` [PATCH v3 0/7] ASoC: codecs: aw88261: fixes and cleanup Mark Brown
  7 siblings, 0 replies; 9+ messages in thread
From: Val Packett @ 2026-05-29 20:05 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai, Weidong Wang
  Cc: Val Packett, Luca Weiss, ~postmarketos/upstreaming, phone-devel,
	linux-sound, linux-kernel

- Invert the value to match userspace expectations (in the hardware,
  positive numbers represent negative dB attenuation)
- Provide TLV metadata for the dB scale (and divide the raw values by 2
  as the excessive precision used by HW is not representable in TLV)
- Do not unnecessarily reset the volume while switching profiles
- Simplify aw88261_dev_set_volume using regmap_update_bits
- Do not add the initial volume from the profile to the requested volume
  as that would throw off the dB mapping (if a lower max limit is
  desired, it can be set in the UCM profile in userspace)

With this change, it's actually possible to use this hardware volume
control as PlaybackVolume in an ALSA UCM profile.

Fixes: 028a2ae25691 ("ASoC: codecs: Add aw88261 amplifier driver")
Signed-off-by: Val Packett <val@packett.cool>
---
 sound/soc/codecs/aw88261.c | 42 +++++++++++++++++++-------------------
 sound/soc/codecs/aw88261.h |  4 ++--
 2 files changed, 23 insertions(+), 23 deletions(-)

diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c
index 01e93bc018b9..d059820282f4 100644
--- a/sound/soc/codecs/aw88261.c
+++ b/sound/soc/codecs/aw88261.c
@@ -15,6 +15,7 @@
 #include <linux/regulator/consumer.h>
 #include <sound/soc.h>
 #include <sound/pcm_params.h>
+#include <sound/tlv.h>
 #include "aw88261.h"
 #include "aw88395/aw88395_data_type.h"
 #include "aw88395/aw88395_device.h"
@@ -29,20 +30,10 @@ static const struct regmap_config aw88261_remap_config = {
 
 static void aw88261_dev_set_volume(struct aw_device *aw_dev, unsigned int value)
 {
-	struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
-	unsigned int real_value, volume;
-	unsigned int reg_value;
+	unsigned int volume = min(value, (unsigned int)AW88261_MUTE_VOL);
 
-	volume = min((value + vol_desc->init_volume), (unsigned int)AW88261_MUTE_VOL);
-	real_value = DB_TO_REG_VAL(volume);
-
-	regmap_read(aw_dev->regmap, AW88261_SYSCTRL2_REG, &reg_value);
-
-	real_value = (real_value | (reg_value & AW88261_VOL_START_MASK));
-
-	dev_dbg(aw_dev->dev, "value 0x%x , real_value:0x%x", value, real_value);
-
-	regmap_write(aw_dev->regmap, AW88261_SYSCTRL2_REG, real_value);
+	regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL2_REG,
+		~AW88261_VOL_MASK, DB_TO_REG_VAL(volume));
 }
 
 static void aw88261_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag)
@@ -994,7 +985,8 @@ static int aw88261_volume_get(struct snd_kcontrol *kcontrol,
 	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
 	struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc;
 
-	ucontrol->value.integer.value[0] = vol_desc->ctl_volume;
+	ucontrol->value.integer.value[0] =
+		(AW88261_MUTE_VOL - vol_desc->ctl_volume) / 2;
 
 	return 0;
 }
@@ -1007,13 +999,13 @@ static int aw88261_volume_set(struct snd_kcontrol *kcontrol,
 	struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc;
 	struct soc_mixer_control *mc =
 		(struct soc_mixer_control *)kcontrol->private_value;
-	int value;
-
-	value = ucontrol->value.integer.value[0];
+	int value = ucontrol->value.integer.value[0];
 
 	if (value < mc->min || value > mc->max)
 		return -EINVAL;
 
+	value = AW88261_MUTE_VOL - (value * 2);
+
 	if (vol_desc->ctl_volume != value) {
 		vol_desc->ctl_volume = value;
 		aw88261_dev_set_volume(aw88261->aw_pa, vol_desc->ctl_volume);
@@ -1024,10 +1016,18 @@ static int aw88261_volume_set(struct snd_kcontrol *kcontrol,
 	return 0;
 }
 
+/*
+ * The field contains 4 bits in units of 6dB + 6 bits in units of 0.125dB
+ * which is too precise for TLV (!) so we have to multiply the scale by 2.
+ *
+ * The range is clamped at -90dB to prevent overflowing the 4-bit part.
+ */
+static const DECLARE_TLV_DB_SCALE(volume_tlv, -9000, 25, 0);
+
 static const struct snd_kcontrol_new aw88261_controls[] = {
-	SOC_SINGLE_EXT("PCM Playback Volume", AW88261_SYSCTRL2_REG,
-		6, AW88261_MUTE_VOL, 0, aw88261_volume_get,
-		aw88261_volume_set),
+	SOC_SINGLE_EXT_TLV("PCM Playback Volume", AW88261_SYSCTRL2_REG,
+		6, AW88261_CTL_MAX_VOL, 1,
+		aw88261_volume_get, aw88261_volume_set, volume_tlv),
 	AW88261_PROFILE_EXT("Profile Set", aw88261_profile_info,
 		aw88261_profile_get, aw88261_profile_set),
 };
@@ -1278,7 +1278,7 @@ static int aw88261_init(struct aw88261 *aw88261, struct i2c_client *i2c, struct
 	aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID;
 	aw_dev->channel = 0;
 	aw_dev->fw_status = AW88261_DEV_FW_FAILED;
-	aw_dev->volume_desc.ctl_volume = AW88261_VOL_DEFAULT_VALUE;
+	aw_dev->volume_desc.ctl_volume = AW88261_CTL_DEFAULT_VOL;
 	aw_dev->volume_desc.mute_volume = AW88261_MUTE_VOL;
 	aw88261_parse_channel_dt(aw88261);
 
diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h
index 4be6ac02123e..270ccf375f36 100644
--- a/sound/soc/codecs/aw88261.h
+++ b/sound/soc/codecs/aw88261.h
@@ -262,7 +262,8 @@
 #define AW88261_VOL_MASK		\
 	(~(((1<<AW88261_VOL_BITS_LEN)-1) << AW88261_VOL_START_BIT))
 
-#define AW88261_VOL_DEFAULT_VALUE	(0)
+#define AW88261_CTL_MAX_VOL		(AW88261_MUTE_VOL / 2)
+#define AW88261_CTL_DEFAULT_VOL		(AW88261_CTL_MAX_VOL / 2)
 
 #define AW88261_I2STXEN_START_BIT	(6)
 #define AW88261_I2STXEN_BITS_LEN	(1)
@@ -538,7 +539,6 @@
 #define AW88261_DEV_SYSST_CHECK_MAX	(10)
 #define AW88261_SOFT_RESET_VALUE	(0x55aa)
 #define AW88261_REG_TO_DB		(0x3f)
-#define AW88261_VOL_START_MASK		(0xfc00)
 #define AW88261_INIT_PROFILE		(0)
 
 #define REG_VAL_TO_DB(value)		((((value) >> AW88261_VOL_6DB_START) * \
-- 
2.53.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 0/7] ASoC: codecs: aw88261: fixes and cleanup
  2026-05-29 20:05 [PATCH v3 0/7] ASoC: codecs: aw88261: fixes and cleanup Val Packett
                   ` (6 preceding siblings ...)
  2026-05-29 20:05 ` [PATCH v3 7/7] ASoC: codecs: aw88261: make volume control usable Val Packett
@ 2026-06-09 23:09 ` Mark Brown
  7 siblings, 0 replies; 9+ messages in thread
From: Mark Brown @ 2026-06-09 23:09 UTC (permalink / raw)
  To: Val Packett
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Luca Weiss,
	~postmarketos/upstreaming, phone-devel, linux-sound,
	linux-kernel

On Fri, 29 May 2026 17:05:07 -0300, Val Packett wrote:
> ASoC: codecs: aw88261: fixes and cleanup
> 
> The Awinic smart speaker/amp drivers were merged in a very
> "downstream-brained" state, where configuration was only really
> determined by the binary "firmware" (register list) file instead
> of properly participating in the ASoC system. Let's start
> untangling this mess. This series makes aw88261 actually usable
> on devices like fairphone-fp5, motorola-dubai and xiaomi-pipa.
> 
> [...]

Applied to

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-7.2

Thanks!

[1/7] ASoC: codecs: aw88261: support changing sample rate and bit width
      https://git.kernel.org/broonie/sound/c/1b92b0673d5e
[2/7] ASoC: codecs: aw88261: add TDM support
      https://git.kernel.org/broonie/sound/c/33f917e18f39
[3/7] ASoC: codecs: aw88261: reduce log spam
      https://git.kernel.org/broonie/sound/c/d90c361af215
[4/7] ASoC: codecs: aw88261: remove fade in/out on start/stop
      https://git.kernel.org/broonie/sound/c/edf01bc1c698
[5/7] ASoC: codecs: aw88261: remove async start
      https://git.kernel.org/broonie/sound/c/caea99ac809d
[6/7] ASoC: codecs: aw88261: fix incorrect masks for boost regs
      https://git.kernel.org/broonie/sound/c/79c053a1ff9d
[7/7] ASoC: codecs: aw88261: make volume control usable
      https://git.kernel.org/broonie/sound/c/9b2929eed4d2

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-06-10 10:23 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-29 20:05 [PATCH v3 0/7] ASoC: codecs: aw88261: fixes and cleanup Val Packett
2026-05-29 20:05 ` [PATCH v3 1/7] ASoC: codecs: aw88261: support changing sample rate and bit width Val Packett
2026-05-29 20:05 ` [PATCH v3 2/7] ASoC: codecs: aw88261: add TDM support Val Packett
2026-05-29 20:05 ` [PATCH v3 3/7] ASoC: codecs: aw88261: reduce log spam Val Packett
2026-05-29 20:05 ` [PATCH v3 4/7] ASoC: codecs: aw88261: remove fade in/out on start/stop Val Packett
2026-05-29 20:05 ` [PATCH v3 5/7] ASoC: codecs: aw88261: remove async start Val Packett
2026-05-29 20:05 ` [PATCH v3 6/7] ASoC: codecs: aw88261: fix incorrect masks for boost regs Val Packett
2026-05-29 20:05 ` [PATCH v3 7/7] ASoC: codecs: aw88261: make volume control usable Val Packett
2026-06-09 23:09 ` [PATCH v3 0/7] ASoC: codecs: aw88261: fixes and cleanup Mark Brown

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®