From: Jack Yu Date: Wed, 20 May 2026 05:32:43 +0000 (+0800) Subject: ASoC: rt722-sdca: Add a control to support CAE firmware update X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=14b5795b633ca0a5af4d3d94cbe2ac98598df18a;p=thirdparty%2Fkernel%2Flinux.git ASoC: rt722-sdca: Add a control to support CAE firmware update Realtek CAE requires specific tuning parameters based on the system vendor and SKU. This patch adds a kcontrol to trigger the firmware loading process. Signed-off-by: Jack Yu Link: https://patch.msgid.link/20260520053243.3645180-1-jack.yu@realtek.com Signed-off-by: Mark Brown --- diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index a5feba3d0c182..0f76492ff915c 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -19,7 +19,9 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg) { switch (reg) { - case 0x2f01 ... 0x2f0a: + case 0x22f0 ... 0x22f1: + case 0x2f01 ... 0x2f0c: + case 0x2f21 ... 0x2f24: case 0x2f35 ... 0x2f36: case 0x2f50 ... 0x2f52: case 0x2f54: @@ -84,6 +86,21 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg) case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0): case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2: + case 0x44011000 ... 0x440115ff: + case 0x44012000: + case 0x44012021: + case 0x44012022: + case 0x44012025: + case 0x44021000 ... 0x440211ff: + case 0x44022000: + case 0x44022019: + case 0x4402201a: + case 0x4402201d: + case 0x44041000 ... 0x440415ff: + case 0x44042000: + case 0x44042019: + case 0x4404201a: + case 0x4404201d: return 1; case 0x2000000 ... 0x2000024: case 0x2000029 ... 0x200004a: @@ -95,8 +112,10 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg) case 0x200007f: case 0x2000082 ... 0x200008e: case 0x2000090 ... 0x2000094: + case 0x20000b1: + case 0x20000b4: case 0x3110000: - case 0x5300000 ... 0x5300002: + case 0x5300000 ... 0x5300300: case 0x5400002: case 0x5600000 ... 0x5600007: case 0x5700000 ... 0x5700004: @@ -175,6 +194,7 @@ static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg) case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0): case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2: case 0x2000000: + case 0x2000007: case 0x200000d: case 0x2000019: case 0x2000020: @@ -186,6 +206,21 @@ static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg) case 0x3110000: case 0x5800003: case 0x5810000: + case 0x44011000 ... 0x440115ff: + case 0x44012000: + case 0x44012021: + case 0x44012022: + case 0x44012025: + case 0x44021000 ... 0x440211ff: + case 0x44022000: + case 0x44022019: + case 0x4402201a: + case 0x4402201d: + case 0x44041000 ... 0x440415ff: + case 0x44042000: + case 0x44042019: + case 0x4404201a: + case 0x4404201d: return true; default: return false; diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index 23d2f63d68ef3..1b6729f363fc4 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -7,19 +7,21 @@ // #include -#include #include +#include +#include #include -#include -#include #include #include #include -#include #include -#include -#include #include +#include +#include +#include +#include +#include +#include #include #include @@ -344,6 +346,280 @@ static int rt722_sdca_set_jack_detect(struct snd_soc_component *component, return 0; } +static int rt722_cae_load(struct rt722_sdca_priv *rt722) +{ + struct device *dev = &rt722->slave->dev; + static const char func_tag[] = "FUNC"; + static const char xu_tag[] = "XU"; + const char *dmi_vendor, *dmi_product, *dmi_sku; + char *cae_filename; + const struct firmware *cae_fw = NULL; + unsigned int cae_st_spk, cae_st_hp, cae_st_mic; + unsigned int func, value; + unsigned int combined_val; + unsigned int addr, size; + unsigned int fw_offset; + unsigned char mbq_high_val = 0; + unsigned char *param_data; + unsigned char *fw_data; + char tag[5]; + char *space; + int v_len, p_len, s_len; + int ret = 0, i; + int retry = 50; + + dmi_vendor = dmi_get_system_info(DMI_SYS_VENDOR); + dmi_product = dmi_get_system_info(DMI_PRODUCT_NAME); + dmi_sku = dmi_get_system_info(DMI_PRODUCT_SKU); + + if (!dmi_vendor || !dmi_product || !dmi_sku) { + dev_warn(dev, "%s: Incomplete DMI info\n", __func__); + return -EINVAL; + } + space = strchr(dmi_vendor, ' '); + v_len = space ? space - dmi_vendor : strlen(dmi_vendor); + + space = strchr(dmi_product, ' '); + p_len = space ? space - dmi_product : strlen(dmi_product); + + space = strchr(dmi_sku, ' '); + s_len = space ? space - dmi_sku : strlen(dmi_sku); + + cae_filename = kasprintf(GFP_KERNEL, + "realtek/rt722/rt722_RAE_%.*s_%.*s_%.*s.dat", + v_len, dmi_vendor, + p_len, dmi_product, + s_len, dmi_sku); + if (!cae_filename) + return -ENOMEM; + dev_dbg(dev, "%s: try to load CAE file %s\n", __func__, cae_filename); + + regmap_write(rt722->regmap, RT722_SPK_CAE_PARAM1, 0x5f); + regmap_write(rt722->regmap, RT722_HP_CAE_PARAM39, 0x5f); + regmap_write(rt722->regmap, RT722_MIC_CAE_PARAM39, 0x5f); + usleep_range(50000, 60000); + + request_firmware(&cae_fw, cae_filename, dev); + kfree(cae_filename); + if (!cae_fw) { + dev_err(dev, "%s: Failed to load CAE firmware\n", __func__); + return -ENOENT; + } + + regmap_read(rt722->regmap, RT722_SPK_CAE_PARAM38, &cae_st_spk); + regmap_read(rt722->regmap, RT722_HP_CAE_PARAM68, &cae_st_hp); + regmap_read(rt722->regmap, RT722_MIC_CAE_PARAM99, &cae_st_mic); + cae_st_spk &= 0x80; + cae_st_hp &= 0x80; + cae_st_mic &= 0x80; + + dev_dbg(dev, "%s(%d) spk_crc:%x, hp_crc:%x, mic_crc:%x\n", + __func__, __LINE__, cae_st_spk, cae_st_hp, cae_st_mic); + + if (cae_st_spk) + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_EQ_CAE, + RT722_EQ_CTRL_SPK, 0x0008, 0x0008); + else if (cae_st_hp) + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_EQ_CAE, + RT722_EQ_CTRL_HP, 0x0008, 0x0008); + else if (cae_st_mic) + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_EQ_CAE, + RT722_EQ_CTRL_DMIC, 0x0008, 0x0008); + + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, + RT722_MISC_CTRL1, 0x8000, 0x8000); + + regmap_update_bits(rt722->regmap, RT722_SPK_CAE_PARAM34, 0x1, 0x0); + regmap_update_bits(rt722->regmap, RT722_HP_CAE_PARAM64, 0x1, 0x0); + regmap_update_bits(rt722->regmap, RT722_MIC_CAE_PARAM95, 0x1, 0x0); + + while (--retry) { + regmap_read(rt722->regmap, RT722_SPK_CAE_PARAM35, &cae_st_spk); + regmap_read(rt722->regmap, RT722_HP_CAE_PARAM65, &cae_st_hp); + regmap_read(rt722->regmap, RT722_MIC_CAE_PARAM96, &cae_st_mic); + dev_dbg(dev, "%s(%d) cae_st_spk:%x, cae_st_hp:%x, cae_st_mic:%x\n", + __func__, __LINE__, cae_st_spk, cae_st_hp, cae_st_mic); + if ((cae_st_spk & 0x40) && (cae_st_hp & 0x40) && (cae_st_mic & 0x40)) + break; + usleep_range(1000, 1100); + } + + if (!retry && !((cae_st_spk & 0x40) && (cae_st_hp & 0x40) + && (cae_st_mic & 0x40))) { + dev_err(dev, "%s: CAE is not ready to be loaded.\n", __func__); + ret = -ETIMEDOUT; + goto out_release; + } + + dev_dbg(dev, "%s, cae_fw size=0x%zx, start\n", __func__, cae_fw->size); + + rt722_sdca_index_write(rt722, RT722_VENDOR_EQ_CAE, + RT722_EQ_CTRL_AMIC, 0x8000); + rt722_sdca_index_write(rt722, RT722_VENDOR_EQ_CAE, + RT722_EQ_CTRL_DMIC, 0x8004); + rt722_sdca_index_write(rt722, RT722_VENDOR_EQ_CAE, + RT722_EQ_CTRL_HP, 0x8074); + rt722_sdca_index_write(rt722, RT722_VENDOR_EQ_CAE, + RT722_EQ_CTRL_SPK, 0xa074); + + regcache_cache_bypass(rt722->regmap, true); + for (fw_offset = 0; fw_offset < cae_fw->size;) { + + if (fw_offset + 12 > cae_fw->size) { + dev_err(dev, "%s: Unexpected end of firmware\n", __func__); + ret = -EINVAL; + goto verify_abort; + } + + fw_data = (unsigned char *)&cae_fw->data[fw_offset]; + memcpy(tag, fw_data, 4); + tag[4] = '\0'; + + if (strcmp(tag, xu_tag) == 0) { + dev_dbg(dev, "%s: This is a XU tag", __func__); + memcpy(&addr, (fw_data + 4), 4); + memcpy(&size, (fw_data + 8), 4); + + if (size == 0 || size > cae_fw->size - fw_offset - 12) { + dev_err(dev, "%s: Invalid payload size: %u\n", __func__, size); + ret = -EINVAL; + goto verify_abort; + } + + param_data = (unsigned char *)(fw_data + 12); + + dev_dbg(dev, "%s: addr=0x%x, size=0x%x\n", __func__, addr, size); + + if ((addr <= 0x05302300 && addr >= 0x05300000) || + (addr <= 0x020020b4 && addr >= 0x020000b1)) { + if (addr & BIT(13)) { + mbq_high_val = param_data[0]; + dev_dbg(dev, "MBQ: High Byte 0x%02x\n", mbq_high_val); + fw_offset += (size + 12); + + continue; + } else { + regcache_cache_bypass(rt722->regmap, false); + combined_val = (mbq_high_val << 8) | param_data[0]; + if (addr == 0x20000b1 || addr == 0x20000b4) + combined_val |= (0x2 << 8); + ret = regmap_write(rt722->regmap, addr, combined_val); + if (ret) { + dev_err(dev, + "MBQ fail: addr=0x%x ret=%d\n", addr, ret); + regcache_cache_bypass(rt722->regmap, true); + goto verify_abort; + } + + dev_dbg(dev, "MBQ-reg=0x%x, value=0x%x\n", + addr, combined_val); + + fw_offset += (size + 12); + regcache_cache_bypass(rt722->regmap, true); + continue; + } + } + + for (i = 0; i < size; i++) { + ret = regmap_write(rt722->regmap, addr + i, param_data[i]); + if (ret) { + dev_err(dev, + "CAE write fail: addr=0x%x ret=%d\n", + addr + i, ret); + goto verify_abort; + } + } + fw_offset += (size + 12); + } else if (strcmp(tag, func_tag) == 0) { + dev_dbg(dev, "%s: This is a FUNC tag", __func__); + + memcpy(&func, (fw_data + 4), 4); + memcpy(&value, (fw_data + 8), 4); + dev_dbg(dev, "%s: func=0x%x, value=0x%x\n", + __func__, func, value); + + if (func == 1) + msleep(value); + + fw_offset += 12; + } else { + dev_err(dev, "%s: No XU/FUNC tag at fw_offset=0x%x\n", + __func__, fw_offset); + ret = -EINVAL; + goto verify_abort; + } + } + + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, + RT722_MISC_CTRL1, 0x8000, 0x0000); + regcache_cache_bypass(rt722->regmap, false); + rt722->cae_update_done = 1; + dev_dbg(dev, "%s: CAE FW update done.\n", __func__); + release_firmware(cae_fw); + return 0; + +verify_abort: + regcache_cache_bypass(rt722->regmap, false); + if (!ret) + ret = -EIO; +out_release: + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, + RT722_MISC_CTRL1, 0x8000, 0x0000); + release_firmware(cae_fw); + dev_err(dev, "%s: CAE FW update aborted (ret=%d).\n", __func__, ret); + return ret; +} + +static int rt722_cae_update_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + int ret, changed = 0; + + if (!rt722->hw_init) + return 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + if (ucontrol->value.integer.value[0]) { + if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) { + ret = rt722_cae_load(rt722); + if (ret) { + dev_err(component->dev, "CAE load failed: %d\n", ret); + goto out; + } else + changed = 1; + } + } else { + if (rt722->cae_update_done) { + rt722->cae_update_done = 0; + changed = 1; + } + } + +out: + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); + + return ret < 0 ? ret : changed; +} + +static int rt722_cae_update_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt722->cae_update_done; + + return 0; +} + + /* For SDCA control DAC/ADC Gain */ static int rt722_sdca_set_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -795,6 +1071,9 @@ static const struct snd_kcontrol_new rt722_sdca_controls[] = { RT722_SDCA_CTL_FU_CH_GAIN, CH_01), rt722_sdca_dmic_set_gain_get, rt722_sdca_dmic_set_gain_put, 4, 3, boost_vol_tlv), + /* CAE firmware update */ + SOC_SINGLE_EXT("CAE Update", SND_SOC_NOPM, 0, 1, 0, + rt722_cae_update_get, rt722_cae_update_put), }; static const char * const adc22_mux_text[] = { @@ -1376,6 +1655,7 @@ int rt722_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave rt722->fu0f_mixer_l_mute = rt722->fu0f_mixer_r_mute = true; rt722->fu1e_mixer_mute[0] = rt722->fu1e_mixer_mute[1] = rt722->fu1e_mixer_mute[2] = rt722->fu1e_mixer_mute[3] = true; + rt722->cae_update_done = 0; return devm_snd_soc_register_component(dev, &soc_sdca_dev_rt722, rt722_sdca_dai, ARRAY_SIZE(rt722_sdca_dai)); diff --git a/sound/soc/codecs/rt722-sdca.h b/sound/soc/codecs/rt722-sdca.h index fc50beff94245..b654b14334d43 100644 --- a/sound/soc/codecs/rt722-sdca.h +++ b/sound/soc/codecs/rt722-sdca.h @@ -44,6 +44,7 @@ struct rt722_sdca_priv { bool fu1e_dapm_mute; bool fu1e_mixer_mute[4]; int hw_vid; + int cae_update_done; }; struct rt722_sdca_dmic_kctrl_priv { @@ -55,6 +56,7 @@ struct rt722_sdca_dmic_kctrl_priv { /* NID */ #define RT722_VENDOR_REG 0x20 +#define RT722_VENDOR_EQ_CAE 0x53 #define RT722_VENDOR_CALI 0x58 #define RT722_VENDOR_SPK_EFUSE 0x5c #define RT722_VENDOR_IMS_DRE 0x5b @@ -64,6 +66,7 @@ struct rt722_sdca_dmic_kctrl_priv { /* Index (NID:20h) */ #define RT722_JD_PRODUCT_NUM 0x00 #define RT722_ANALOG_BIAS_CTL3 0x04 +#define RT722_MISC_CTRL1 0x07 #define RT722_JD_CTRL1 0x09 #define RT722_LDO2_3_CTL1 0x0e #define RT722_LDO1_CTL 0x1a @@ -79,6 +82,12 @@ struct rt722_sdca_dmic_kctrl_priv { #define RT722_SW_CONFIG1 0x8a #define RT722_SW_CONFIG2 0x8b +/* Index (NID:53h) */ +#define RT722_EQ_CTRL_SPK 0x00 +#define RT722_EQ_CTRL_HP 0x100 +#define RT722_EQ_CTRL_DMIC 0x200 +#define RT722_EQ_CTRL_AMIC 0x300 + /* Index (NID:58h) */ #define RT722_DAC_DC_CALI_CTL0 0x00 #define RT722_DAC_DC_CALI_CTL1 0x01 @@ -156,6 +165,20 @@ struct rt722_sdca_dmic_kctrl_priv { #define RT722_BUF_ADDR_HID1 0x44030000 #define RT722_BUF_ADDR_HID2 0x44030020 +/* RT722 CAE parameter settings */ +#define RT722_SPK_CAE_PARAM1 0x44012000 +#define RT722_SPK_CAE_PARAM34 0x44012021 +#define RT722_SPK_CAE_PARAM35 0x44012022 +#define RT722_SPK_CAE_PARAM38 0x44012025 +#define RT722_HP_CAE_PARAM39 0x44022000 +#define RT722_HP_CAE_PARAM64 0x44022019 +#define RT722_HP_CAE_PARAM65 0x4402201a +#define RT722_HP_CAE_PARAM68 0x4402201d +#define RT722_MIC_CAE_PARAM39 0x44042000 +#define RT722_MIC_CAE_PARAM95 0x44042019 +#define RT722_MIC_CAE_PARAM96 0x4404201a +#define RT722_MIC_CAE_PARAM99 0x4404201d + /* RT722 SDCA Control - function number */ #define FUNC_NUM_JACK_CODEC 0x01 #define FUNC_NUM_MIC_ARRAY 0x02