mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Georg Wenzel <g.wenzel@gmail.com>
To: shenghao-ding@ti.com, kevin-lu@ti.com, baojun.xu@ti.com, sen@ti.com
Cc: perex@perex.cz, tiwai@suse.com, linux-sound@vger.kernel.org,
	linux-kernel@vger.kernel.org, Georg Wenzel <g.wenzel@gmail.com>
Subject: [PATCH] ALSA: hda/tas2781: Enable bass speakers support
Date: Mon, 21 Sep 2026 20:18:44 +0200	[thread overview]
Message-ID: <20260921181845.17923-1-g.wenzel@gmail.com> (raw)

Configure the driver to correctly initialize and enable the bass speakers
on devices utilizing the TAS2781 amplifier. Without this change, only the
tweeters or main speakers are active, leading to thin audio output.

Set up the required register sequences and routing to ensure the bass
channels are properly powered and mixed into the audio stream.

Signed-off-by: Georg Wenzel <g.wenzel@gmail.com>
---
 include/sound/tas2781.h                       | 12 +++
 .../hda/codecs/side-codecs/tas2781_hda_i2c.c  | 97 +++++++++++++++++++
 2 files changed, 109 insertions(+)

diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h
index b763da613769..ccc0142fbad5 100644
--- a/include/sound/tas2781.h
+++ b/include/sound/tas2781.h
@@ -91,6 +91,18 @@
 /* prm_TE_1_Beta1 */
 #define TAS2563_TE_DT_REG		TASDEVICE_REG(0x00, 0x0f, 0x70)
 
+/* TAS2563 ASI (Audio Serial Interface) Register Definitions */
+#define TAS2563_ASI_CFG_REG			TASDEVICE_REG(0x00, 0x00, 0x06)
+#define TAS2563_ASI_SR_REG			TASDEVICE_REG(0x00, 0x00, 0x07)
+#define TAS2563_ASI_FS_REG			TASDEVICE_REG(0x00, 0x00, 0x08)
+#define TAS2563_ASI_DA_REG			TASDEVICE_REG(0x00, 0x00, 0x0a)
+#define TAS2563_ASI_TDM_REG			TASDEVICE_REG(0x00, 0x00, 0x0b)
+#define TAS2563_ASI_DL_REG			TASDEVICE_REG(0x00, 0x00, 0x0c)
+#define TAS2563_ASI_SLOT_REG		TASDEVICE_REG(0x00, 0x00, 0x30)
+
+/* TAS2563 ASI Apply Register (triggers configuration update) */
+#define TAS2563_ASI_APPLY_REG			TASDEVICE_REG(0x00, 0x00, 0x02)
+
 #define TAS2781_PRM_INT_MASK_REG	TASDEVICE_REG(0x00, 0x00, 0x3b)
 #define TAS2781_PRM_CLK_CFG_REG		TASDEVICE_REG(0x00, 0x00, 0x5c)
 #define TAS2781_PRM_RSVD_REG		TASDEVICE_REG(0x00, 0x01, 0x19)
diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
index 624db967f17b..101d6cee6da2 100644
--- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
+++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
@@ -46,6 +46,83 @@
 #define TAS2563_CAL_TLIM		TASDEVICE_REG(0, 0x10, 0x14)
 #define TAS2563_CAL_R0			TASDEVICE_REG(0, 0x0f, 0x34)
 
+/*
+ * TAS2563 Post-Firmware ASI Configuration.
+ *
+ * The DSP firmware loading can overwrite the ASI (Audio Serial Interface)
+ * registers that configure the I2S/TDM audio path to the amplifier.
+ * This function re-applies the correct ASI configuration after firmware load,
+ * matching the reference initialization sequence used by the vendor script:
+ *   - Software reset
+ *   - ASI_CFG  = 0x09 (I2S mode, master/slave config)
+ *   - ASI_SR   = 0x02 (sample rate divider for 48 kHz)
+ *   - ASI_DA   = 0xf1 (data alignment / slot assignment)
+ *   - ASI_TDM  = 0x02 (TDM mode select)
+ *   - ASI_DL   = 0x00 (delay line off)
+ *   - ASI_SLOT = 0x99 (slot enable mask for 2-channel stereo)
+ *   - CHNL_0   = 0x02 (channel mapping)
+ *   - ASI_FS   = 0x1e or 0x2e (frame sync, device-specific: bit4 selects
+ *                which of the two devices is the "primary" for FS generation)
+ *   - ASI_APPLY= 0x00 (trigger configuration update)
+ */
+static void tas2563_post_fw_init(struct tasdevice_priv *tas_priv)
+{
+	int i, ret;
+
+	dev_info(tas_priv->dev, "%s: ndev=%d, chip_id=%d\n",
+		 __func__, tas_priv->ndev, tas_priv->chip_id);
+
+	for (i = 0; i < tas_priv->ndev; i++) {
+		struct tasdevice *tasdev = &tas_priv->tasdevice[i];
+		u8 fs_val;
+
+		dev_info(tas_priv->dev,
+			 "%s: device[%d] dev_addr=0x%02x\n",
+			 __func__, i, tasdev->dev_addr);
+
+		/* Step 1: Software reset (matches script: 0x01 = 0x01) */
+		ret = tasdevice_dev_write(tas_priv, i, TASDEVICE_REG_SWRESET,
+					  TASDEVICE_REG_SWRESET_RESET);
+		dev_info(tas_priv->dev,
+			 "%s: chn=%d SWRESET ret=%d\n", __func__, i, ret);
+		usleep_range(1000, 2000);
+
+		/* Step 2: ASI configuration registers */
+		ret = tasdevice_dev_write(tas_priv, i, TAS2563_ASI_CFG_REG, 0x09);
+		dev_info(tas_priv->dev, "%s: chn=%d ASI_CFG=0x09 ret=%d\n", __func__, i, ret);
+
+		ret = tasdevice_dev_write(tas_priv, i, TAS2563_ASI_SR_REG, 0x02);
+		dev_info(tas_priv->dev, "%s: chn=%d ASI_SR=0x02 ret=%d\n", __func__, i, ret);
+
+		ret = tasdevice_dev_write(tas_priv, i, TAS2563_ASI_DA_REG, 0xf1);
+		dev_info(tas_priv->dev, "%s: chn=%d ASI_DA=0xf1 ret=%d\n", __func__, i, ret);
+
+		ret = tasdevice_dev_write(tas_priv, i, TAS2563_ASI_TDM_REG, 0x02);
+		dev_info(tas_priv->dev, "%s: chn=%d ASI_TDM=0x02 ret=%d\n", __func__, i, ret);
+
+		ret = tasdevice_dev_write(tas_priv, i, TAS2563_ASI_DL_REG, 0x00);
+		dev_info(tas_priv->dev, "%s: chn=%d ASI_DL=0x00 ret=%d\n", __func__, i, ret);
+
+		ret = tasdevice_dev_write(tas_priv, i, TAS2563_ASI_SLOT_REG, 0x99);
+		dev_info(tas_priv->dev, "%s: chn=%d ASI_SLOT=0x99 ret=%d\n", __func__, i, ret);
+
+		ret = tasdevice_dev_write(tas_priv, i, TAS2781_PRM_CHNL_0_REG, 0x02);
+		dev_info(tas_priv->dev, "%s: chn=%d CHNL_0=0x02 ret=%d\n", __func__, i, ret);
+
+		/* Step 3: Device-specific frame sync (0x1e for dev0, 0x2e for dev1) */
+		fs_val = 0x1e | ((i & 0x01) << 4);
+		ret = tasdevice_dev_write(tas_priv, i, TAS2563_ASI_FS_REG, fs_val);
+		dev_info(tas_priv->dev, "%s: chn=%d ASI_FS=0x%02x ret=%d\n",
+				__func__, i, fs_val, ret);
+
+		/* Step 4: Apply ASI configuration */
+		ret = tasdevice_dev_write(tas_priv, i, TAS2563_ASI_APPLY_REG, 0x00);
+		dev_info(tas_priv->dev, "%s: chn=%d ASI_APPLY ret=%d\n", __func__, i, ret);
+
+		usleep_range(1000, 2000);
+	}
+}
+
 enum device_chip_id {
 	HDA_TAS2563,
 	HDA_TAS2770,
@@ -174,6 +251,12 @@ static void tas2781_hda_playback_hook(struct device *dev, int action)
 		pm_runtime_get_sync(dev);
 		scoped_guard(mutex, &tas_hda->priv->codec_lock) {
 			tasdevice_tuning_switch(tas_hda->priv, 0);
+			/* Re-apply ASI configuration for TAS2563 after
+			 * tuning switch — DSP firmware loading can
+			 * overwrite ASI registers.
+			 */
+			if (tas_hda->priv->chip_id == TAS2563)
+				tas2563_post_fw_init(tas_hda->priv);
 			tas_hda->priv->playback_started = true;
 		}
 		break;
@@ -477,6 +560,12 @@ static void tasdevice_dspfw_init(void *context)
 			tas_priv->rcabin.init_profile_id,
 			TASDEVICE_BIN_BLK_PRE_POWER_UP);
 
+	/* Apply ASI configuration for TAS2563 after firmware load.
+	 * DSP firmware loading can overwrite ASI registers.
+	 */
+	if (tas_hda->priv->chip_id == TAS2563)
+		tas2563_post_fw_init(tas_hda->priv);
+
 	/* If calibrated data occurs error, dsp will still works with default
 	 * calibrated data inside algo.
 	 */
@@ -661,6 +750,7 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt)
 		 */
 		device_name = "INT8866";
 		hda_priv->hda_chip_id = HDA_TAS2563;
+		tas_hda->priv->chip_id = TAS2563;
 		hda_priv->save_calibration = tas2563_save_calibration;
 		tas_hda->priv->global_addr = TAS2563_GLOBAL_ADDR;
 	} else if (strstarts(dev_name(&clt->dev), "i2c-TXNW5825")) {
@@ -784,6 +874,13 @@ static int tas2781_system_resume(struct device *dev)
 			tas_hda->priv->rcabin.init_profile_id,
 			TASDEVICE_BIN_BLK_PRE_POWER_UP);
 
+	/* Re-apply ASI configuration for TAS2563 after system sleep
+	 * resume — amplifiers may lose their register state during
+	 * suspend-to-ram or suspend-to-disk regardless of playback state.
+	 */
+	if (tas_hda->priv->chip_id == TAS2563)
+		tas2563_post_fw_init(tas_hda->priv);
+
 	if (tas_hda->priv->playback_started)
 		tasdevice_tuning_switch(tas_hda->priv, 0);
 
-- 
2.55.0


                 reply	other threads:[~2026-09-21 18:18 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=20260921181845.17923-1-g.wenzel@gmail.com \
    --to=g.wenzel@gmail.com \
    --cc=baojun.xu@ti.com \
    --cc=kevin-lu@ti.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-sound@vger.kernel.org \
    --cc=perex@perex.cz \
    --cc=sen@ti.com \
    --cc=shenghao-ding@ti.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 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®