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 5e5f46c9fb6b..cef33159eba4 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, @@ -172,6 +249,13 @@ 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, false); + /* 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; @@ -475,6 +559,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. */ @@ -659,6 +749,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")) { @@ -782,6 +873,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, false);