Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ALSA: cs35l41: Add shared boost feature

Shared boost allows two amplifiers to share a single boost circuit by
communicating on the MDSYNC bus.
The passive amplifier does not control the boost and receives data from
the active amplifier.

Shared Boost is not supported in HDA Systems.
Based on David Rhodes shared boost patches.

Signed-off-by: Lucas Tanure <lucas.tanure@collabora.com>
Reviewed-by: David Rhodes <david.rhodes@cirrus.com>
Link: https://lore.kernel.org/r/20230223084324.9076-4-lucas.tanure@collabora.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Lucas Tanure and committed by
Mark Brown
f5030564 be9457f1

+113 -7
+12 -1
include/sound/cs35l41.h
··· 11 11 #define __CS35L41_H 12 12 13 13 #include <linux/regmap.h> 14 + #include <linux/completion.h> 14 15 #include <linux/firmware/cirrus/cs_dsp.h> 15 16 16 17 #define CS35L41_FIRSTREG 0x00000000 ··· 678 677 679 678 #define CS35L36_PUP_DONE_IRQ_UNMASK 0x5F 680 679 #define CS35L36_PUP_DONE_IRQ_MASK 0xBF 680 + #define CS35L41_SYNC_EN_MASK BIT(8) 681 681 682 682 #define CS35L41_AMP_SHORT_ERR 0x80000000 683 683 #define CS35L41_BST_SHORT_ERR 0x0100 ··· 688 686 #define CS35L41_BST_DCM_UVP_ERR 0x80 689 687 #define CS35L41_OTP_BOOT_DONE 0x02 690 688 #define CS35L41_PLL_UNLOCK 0x10 689 + #define CS35L41_PLL_LOCK BIT(1) 691 690 #define CS35L41_OTP_BOOT_ERR 0x80000000 692 691 693 692 #define CS35L41_AMP_SHORT_ERR_RLS 0x02 ··· 708 705 #define CS35L41_INT1_MASK_DEFAULT 0x7FFCFE3F 709 706 #define CS35L41_INT1_UNMASK_PUP 0xFEFFFFFF 710 707 #define CS35L41_INT1_UNMASK_PDN 0xFF7FFFFF 708 + #define CS35L41_INT3_PLL_LOCK_SHIFT 1 709 + #define CS35L41_INT3_PLL_LOCK_MASK BIT(CS35L41_INT3_PLL_LOCK_SHIFT) 711 710 712 711 #define CS35L41_GPIO_DIR_MASK 0x80000000 713 712 #define CS35L41_GPIO_DIR_SHIFT 31 ··· 747 742 enum cs35l41_boost_type { 748 743 CS35L41_INT_BOOST, 749 744 CS35L41_EXT_BOOST, 745 + CS35L41_SHD_BOOST_ACTV, 746 + CS35L41_SHD_BOOST_PASS, 747 + 748 + // Not present in Binding Documentation, so no system should use this value. 749 + // This value is only used in CLSA0100 Laptop 750 750 CS35L41_EXT_BOOST_NO_VSPK_SWITCH, 751 751 }; 752 752 ··· 901 891 int cs35l41_init_boost(struct device *dev, struct regmap *regmap, 902 892 struct cs35l41_hw_cfg *hw_cfg); 903 893 bool cs35l41_safe_reset(struct regmap *regmap, enum cs35l41_boost_type b_type); 904 - int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable); 894 + int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable, 895 + struct completion *pll_lock); 905 896 906 897 #endif /* __CS35L41_H */
+3 -3
sound/pci/hda/cs35l41_hda.c
··· 514 514 break; 515 515 case HDA_GEN_PCM_ACT_PREPARE: 516 516 mutex_lock(&cs35l41->fw_mutex); 517 - ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 1); 517 + ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 1, NULL); 518 518 mutex_unlock(&cs35l41->fw_mutex); 519 519 break; 520 520 case HDA_GEN_PCM_ACT_CLEANUP: 521 521 mutex_lock(&cs35l41->fw_mutex); 522 522 regmap_multi_reg_write(reg, cs35l41_hda_mute, ARRAY_SIZE(cs35l41_hda_mute)); 523 - ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 0); 523 + ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 0, NULL); 524 524 mutex_unlock(&cs35l41->fw_mutex); 525 525 break; 526 526 case HDA_GEN_PCM_ACT_CLOSE: ··· 672 672 if (cs35l41->playback_started) { 673 673 regmap_multi_reg_write(cs35l41->regmap, cs35l41_hda_mute, 674 674 ARRAY_SIZE(cs35l41_hda_mute)); 675 - cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0); 675 + cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0, NULL); 676 676 regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2, 677 677 CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT); 678 678 if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
+72 -1
sound/soc/codecs/cs35l41-lib.c
··· 1114 1114 { 0x00000040, 0x00000033 }, 1115 1115 }; 1116 1116 1117 + static const struct reg_sequence cs35l41_actv_seq[] = { 1118 + /* SYNC_BST_CTL_RX_EN = 1; SYNC_BST_CTL_TX_EN = 1 */ 1119 + {CS35L41_MDSYNC_EN, 0x00003000}, 1120 + /* BST_CTL_SEL = MDSYNC */ 1121 + {CS35L41_BSTCVRT_VCTRL2, 0x00000002}, 1122 + }; 1123 + 1124 + static const struct reg_sequence cs35l41_pass_seq[] = { 1125 + /* SYNC_BST_CTL_RX_EN = 0; SYNC_BST_CTL_TX_EN = 1 */ 1126 + {CS35L41_MDSYNC_EN, 0x00001000}, 1127 + /* BST_EN = 0 */ 1128 + {CS35L41_PWR_CTRL2, 0x00003300}, 1129 + /* BST_CTL_SEL = MDSYNC */ 1130 + {CS35L41_BSTCVRT_VCTRL2, 0x00000002}, 1131 + }; 1132 + 1117 1133 int cs35l41_init_boost(struct device *dev, struct regmap *regmap, 1118 1134 struct cs35l41_hw_cfg *hw_cfg) 1119 1135 { 1120 1136 int ret; 1121 1137 1122 1138 switch (hw_cfg->bst_type) { 1139 + case CS35L41_SHD_BOOST_ACTV: 1140 + regmap_multi_reg_write(regmap, cs35l41_actv_seq, ARRAY_SIZE(cs35l41_actv_seq)); 1141 + fallthrough; 1123 1142 case CS35L41_INT_BOOST: 1124 1143 ret = cs35l41_boost_config(dev, regmap, hw_cfg->bst_ind, 1125 1144 hw_cfg->bst_cap, hw_cfg->bst_ipk); ··· 1156 1137 ARRAY_SIZE(cs35l41_reset_to_safe)); 1157 1138 ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL2, CS35L41_BST_EN_MASK, 1158 1139 CS35L41_BST_DIS_FET_OFF << CS35L41_BST_EN_SHIFT); 1140 + break; 1141 + case CS35L41_SHD_BOOST_PASS: 1142 + ret = regmap_multi_reg_write(regmap, cs35l41_pass_seq, 1143 + ARRAY_SIZE(cs35l41_pass_seq)); 1159 1144 break; 1160 1145 default: 1161 1146 dev_err(dev, "Boost type %d not supported\n", hw_cfg->bst_type); ··· 1188 1165 } 1189 1166 EXPORT_SYMBOL_GPL(cs35l41_safe_reset); 1190 1167 1191 - int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable) 1168 + int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable, 1169 + struct completion *pll_lock) 1192 1170 { 1193 1171 int ret; 1172 + unsigned int gpio1_func, pad_control, pwr_ctrl1, pwr_ctrl3; 1173 + struct reg_sequence cs35l41_mdsync_down_seq[] = { 1174 + {CS35L41_PWR_CTRL3, 0}, 1175 + {CS35L41_GPIO_PAD_CONTROL, 0}, 1176 + {CS35L41_PWR_CTRL1, 0, 3000}, 1177 + }; 1178 + struct reg_sequence cs35l41_mdsync_up_seq[] = { 1179 + {CS35L41_PWR_CTRL3, 0}, 1180 + {CS35L41_PWR_CTRL1, 0x00000000, 3000}, 1181 + {CS35L41_PWR_CTRL1, 0x00000001, 3000}, 1182 + }; 1194 1183 1195 1184 switch (b_type) { 1185 + case CS35L41_SHD_BOOST_ACTV: 1186 + case CS35L41_SHD_BOOST_PASS: 1187 + regmap_read(regmap, CS35L41_PWR_CTRL3, &pwr_ctrl3); 1188 + regmap_read(regmap, CS35L41_GPIO_PAD_CONTROL, &pad_control); 1189 + 1190 + pwr_ctrl3 &= ~CS35L41_SYNC_EN_MASK; 1191 + pwr_ctrl1 = enable << CS35L41_GLOBAL_EN_SHIFT; 1192 + 1193 + gpio1_func = enable ? CS35L41_GPIO1_MDSYNC : CS35L41_GPIO1_HIZ; 1194 + gpio1_func <<= CS35L41_GPIO1_CTRL_SHIFT; 1195 + 1196 + pad_control &= ~CS35L41_GPIO1_CTRL_MASK; 1197 + pad_control |= gpio1_func & CS35L41_GPIO1_CTRL_MASK; 1198 + 1199 + cs35l41_mdsync_down_seq[0].def = pwr_ctrl3; 1200 + cs35l41_mdsync_down_seq[1].def = pad_control; 1201 + cs35l41_mdsync_down_seq[2].def = pwr_ctrl1; 1202 + ret = regmap_multi_reg_write(regmap, cs35l41_mdsync_down_seq, 1203 + ARRAY_SIZE(cs35l41_mdsync_down_seq)); 1204 + if (!enable) 1205 + break; 1206 + 1207 + if (!pll_lock) 1208 + return -EINVAL; 1209 + 1210 + ret = wait_for_completion_timeout(pll_lock, msecs_to_jiffies(1000)); 1211 + if (ret == 0) { 1212 + ret = -ETIMEDOUT; 1213 + } else { 1214 + regmap_read(regmap, CS35L41_PWR_CTRL3, &pwr_ctrl3); 1215 + pwr_ctrl3 |= CS35L41_SYNC_EN_MASK; 1216 + cs35l41_mdsync_up_seq[0].def = pwr_ctrl3; 1217 + ret = regmap_multi_reg_write(regmap, cs35l41_mdsync_up_seq, 1218 + ARRAY_SIZE(cs35l41_mdsync_up_seq)); 1219 + } 1220 + break; 1196 1221 case CS35L41_INT_BOOST: 1197 1222 ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL1, CS35L41_GLOBAL_EN_MASK, 1198 1223 enable << CS35L41_GLOBAL_EN_SHIFT);
+25 -2
sound/soc/codecs/cs35l41.c
··· 360 360 { 361 361 switch (cs35l41->hw_cfg.bst_type) { 362 362 case CS35L41_INT_BOOST: 363 + case CS35L41_SHD_BOOST_ACTV: 363 364 enable = enable ? CS35L41_BST_EN_DEFAULT : CS35L41_BST_DIS_FET_OFF; 364 365 regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2, CS35L41_BST_EN_MASK, 365 366 enable << CS35L41_BST_EN_SHIFT); ··· 456 455 ret = IRQ_HANDLED; 457 456 } 458 457 458 + if (status[2] & CS35L41_PLL_LOCK) { 459 + regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS3, CS35L41_PLL_LOCK); 460 + complete(&cs35l41->pll_lock); 461 + ret = IRQ_HANDLED; 462 + } 463 + 459 464 done: 460 465 pm_runtime_mark_last_busy(cs35l41->dev); 461 466 pm_runtime_put_autosuspend(cs35l41->dev); ··· 499 492 cs35l41_pup_patch, 500 493 ARRAY_SIZE(cs35l41_pup_patch)); 501 494 502 - cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 1); 495 + cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 1, 496 + &cs35l41->pll_lock); 503 497 break; 504 498 case SND_SOC_DAPM_POST_PMD: 505 - cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0); 499 + cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0, 500 + &cs35l41->pll_lock); 506 501 507 502 ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS1, 508 503 val, val & CS35L41_PDN_DONE_MASK, ··· 811 802 static int cs35l41_pcm_startup(struct snd_pcm_substream *substream, 812 803 struct snd_soc_dai *dai) 813 804 { 805 + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component); 806 + 807 + reinit_completion(&cs35l41->pll_lock); 808 + 814 809 if (substream->runtime) 815 810 return snd_pcm_hw_constraint_list(substream->runtime, 0, 816 811 SNDRV_PCM_HW_PARAM_RATE, ··· 1265 1252 /* Set interrupt masks for critical errors */ 1266 1253 regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 1267 1254 CS35L41_INT1_MASK_DEFAULT); 1255 + if (cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_PASS || 1256 + cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_ACTV) 1257 + regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK3, CS35L41_INT3_PLL_LOCK_MASK, 1258 + 0 << CS35L41_INT3_PLL_LOCK_SHIFT); 1268 1259 1269 1260 ret = devm_request_threaded_irq(cs35l41->dev, cs35l41->irq, NULL, cs35l41_irq, 1270 1261 IRQF_ONESHOT | IRQF_SHARED | irq_pol, ··· 1291 1274 ret = cs35l41_dsp_init(cs35l41); 1292 1275 if (ret < 0) 1293 1276 goto err; 1277 + 1278 + init_completion(&cs35l41->pll_lock); 1294 1279 1295 1280 pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000); 1296 1281 pm_runtime_use_autosuspend(cs35l41->dev); ··· 1336 1317 pm_runtime_disable(cs35l41->dev); 1337 1318 1338 1319 regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF); 1320 + if (cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_PASS || 1321 + cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_ACTV) 1322 + regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK3, CS35L41_INT3_PLL_LOCK_MASK, 1323 + 1 << CS35L41_INT3_PLL_LOCK_SHIFT); 1339 1324 kfree(cs35l41->dsp.system_name); 1340 1325 wm_adsp2_remove(&cs35l41->dsp); 1341 1326 cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);
+1
sound/soc/codecs/cs35l41.h
··· 33 33 int irq; 34 34 /* GPIO for /RST */ 35 35 struct gpio_desc *reset_gpio; 36 + struct completion pll_lock; 36 37 }; 37 38 38 39 int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *hw_cfg);