| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * ALSA SoC TLV320AIC3X codec driver |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 5 | 6 | * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> |
|---|
| 6 | 7 | * |
|---|
| 7 | 8 | * Based on sound/soc/codecs/wm8753.c by Liam Girdwood |
|---|
| 8 | | - * |
|---|
| 9 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 10 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 11 | | - * published by the Free Software Foundation. |
|---|
| 12 | 9 | * |
|---|
| 13 | 10 | * Notes: |
|---|
| 14 | 11 | * The AIC3X is a driver for a low power stereo audio |
|---|
| .. | .. |
|---|
| 324 | 321 | */ |
|---|
| 325 | 322 | static DECLARE_TLV_DB_SCALE(output_stage_tlv, -5900, 50, 1); |
|---|
| 326 | 323 | |
|---|
| 324 | +/* Output volumes. From 0 to 9 dB in 1 dB steps */ |
|---|
| 325 | +static const DECLARE_TLV_DB_SCALE(out_tlv, 0, 100, 0); |
|---|
| 326 | + |
|---|
| 327 | 327 | static const struct snd_kcontrol_new aic3x_snd_controls[] = { |
|---|
| 328 | 328 | /* Output */ |
|---|
| 329 | 329 | SOC_DOUBLE_R_TLV("PCM Playback Volume", |
|---|
| .. | .. |
|---|
| 386 | 386 | DACL1_2_HPLCOM_VOL, DACR1_2_HPRCOM_VOL, |
|---|
| 387 | 387 | 0, 118, 1, output_stage_tlv), |
|---|
| 388 | 388 | |
|---|
| 389 | | - /* Output pin mute controls */ |
|---|
| 389 | + /* Output pin controls */ |
|---|
| 390 | + SOC_DOUBLE_R_TLV("Line Playback Volume", LLOPM_CTRL, RLOPM_CTRL, 4, |
|---|
| 391 | + 9, 0, out_tlv), |
|---|
| 390 | 392 | SOC_DOUBLE_R("Line Playback Switch", LLOPM_CTRL, RLOPM_CTRL, 3, |
|---|
| 391 | 393 | 0x01, 0), |
|---|
| 394 | + SOC_DOUBLE_R_TLV("HP Playback Volume", HPLOUT_CTRL, HPROUT_CTRL, 4, |
|---|
| 395 | + 9, 0, out_tlv), |
|---|
| 392 | 396 | SOC_DOUBLE_R("HP Playback Switch", HPLOUT_CTRL, HPROUT_CTRL, 3, |
|---|
| 393 | 397 | 0x01, 0), |
|---|
| 398 | + SOC_DOUBLE_R_TLV("HPCOM Playback Volume", HPLCOM_CTRL, HPRCOM_CTRL, |
|---|
| 399 | + 4, 9, 0, out_tlv), |
|---|
| 394 | 400 | SOC_DOUBLE_R("HPCOM Playback Switch", HPLCOM_CTRL, HPRCOM_CTRL, 3, |
|---|
| 395 | 401 | 0x01, 0), |
|---|
| 396 | 402 | |
|---|
| .. | .. |
|---|
| 472 | 478 | 0, 118, 1, output_stage_tlv), |
|---|
| 473 | 479 | |
|---|
| 474 | 480 | SOC_SINGLE("Mono Playback Switch", MONOLOPM_CTRL, 3, 0x01, 0), |
|---|
| 481 | + SOC_SINGLE_TLV("Mono Playback Volume", MONOLOPM_CTRL, 4, 9, 0, |
|---|
| 482 | + out_tlv), |
|---|
| 483 | + |
|---|
| 475 | 484 | }; |
|---|
| 476 | 485 | |
|---|
| 477 | 486 | /* |
|---|
| .. | .. |
|---|
| 1047 | 1056 | width = params_width(params); |
|---|
| 1048 | 1057 | |
|---|
| 1049 | 1058 | /* select data word length */ |
|---|
| 1050 | | - data = snd_soc_component_read32(component, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4)); |
|---|
| 1059 | + data = snd_soc_component_read(component, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4)); |
|---|
| 1051 | 1060 | switch (width) { |
|---|
| 1052 | 1061 | case 16: |
|---|
| 1053 | 1062 | break; |
|---|
| .. | .. |
|---|
| 1207 | 1216 | return 0; |
|---|
| 1208 | 1217 | } |
|---|
| 1209 | 1218 | |
|---|
| 1210 | | -static int aic3x_mute(struct snd_soc_dai *dai, int mute) |
|---|
| 1219 | +static int aic3x_mute(struct snd_soc_dai *dai, int mute, int direction) |
|---|
| 1211 | 1220 | { |
|---|
| 1212 | 1221 | struct snd_soc_component *component = dai->component; |
|---|
| 1213 | | - u8 ldac_reg = snd_soc_component_read32(component, LDAC_VOL) & ~MUTE_ON; |
|---|
| 1214 | | - u8 rdac_reg = snd_soc_component_read32(component, RDAC_VOL) & ~MUTE_ON; |
|---|
| 1222 | + u8 ldac_reg = snd_soc_component_read(component, LDAC_VOL) & ~MUTE_ON; |
|---|
| 1223 | + u8 rdac_reg = snd_soc_component_read(component, RDAC_VOL) & ~MUTE_ON; |
|---|
| 1215 | 1224 | |
|---|
| 1216 | 1225 | if (mute) { |
|---|
| 1217 | 1226 | snd_soc_component_write(component, LDAC_VOL, ldac_reg | MUTE_ON); |
|---|
| .. | .. |
|---|
| 1247 | 1256 | struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component); |
|---|
| 1248 | 1257 | u8 iface_areg, iface_breg; |
|---|
| 1249 | 1258 | |
|---|
| 1250 | | - iface_areg = snd_soc_component_read32(component, AIC3X_ASD_INTF_CTRLA) & 0x3f; |
|---|
| 1251 | | - iface_breg = snd_soc_component_read32(component, AIC3X_ASD_INTF_CTRLB) & 0x3f; |
|---|
| 1259 | + iface_areg = snd_soc_component_read(component, AIC3X_ASD_INTF_CTRLA) & 0x3f; |
|---|
| 1260 | + iface_breg = snd_soc_component_read(component, AIC3X_ASD_INTF_CTRLB) & 0x3f; |
|---|
| 1252 | 1261 | |
|---|
| 1253 | 1262 | /* set master/slave audio interface */ |
|---|
| 1254 | 1263 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
|---|
| .. | .. |
|---|
| 1259 | 1268 | case SND_SOC_DAIFMT_CBS_CFS: |
|---|
| 1260 | 1269 | aic3x->master = 0; |
|---|
| 1261 | 1270 | iface_areg &= ~(BIT_CLK_MASTER | WORD_CLK_MASTER); |
|---|
| 1271 | + break; |
|---|
| 1272 | + case SND_SOC_DAIFMT_CBM_CFS: |
|---|
| 1273 | + aic3x->master = 1; |
|---|
| 1274 | + iface_areg |= BIT_CLK_MASTER; |
|---|
| 1275 | + iface_areg &= ~WORD_CLK_MASTER; |
|---|
| 1276 | + break; |
|---|
| 1277 | + case SND_SOC_DAIFMT_CBS_CFM: |
|---|
| 1278 | + aic3x->master = 1; |
|---|
| 1279 | + iface_areg |= WORD_CLK_MASTER; |
|---|
| 1280 | + iface_areg &= ~BIT_CLK_MASTER; |
|---|
| 1262 | 1281 | break; |
|---|
| 1263 | 1282 | default: |
|---|
| 1264 | 1283 | return -EINVAL; |
|---|
| .. | .. |
|---|
| 1388 | 1407 | * writing one of them and thus caused other one also not |
|---|
| 1389 | 1408 | * being written |
|---|
| 1390 | 1409 | */ |
|---|
| 1391 | | - pll_c = snd_soc_component_read32(component, AIC3X_PLL_PROGC_REG); |
|---|
| 1392 | | - pll_d = snd_soc_component_read32(component, AIC3X_PLL_PROGD_REG); |
|---|
| 1410 | + pll_c = snd_soc_component_read(component, AIC3X_PLL_PROGC_REG); |
|---|
| 1411 | + pll_d = snd_soc_component_read(component, AIC3X_PLL_PROGD_REG); |
|---|
| 1393 | 1412 | if (pll_c == aic3x_reg[AIC3X_PLL_PROGC_REG].def || |
|---|
| 1394 | 1413 | pll_d == aic3x_reg[AIC3X_PLL_PROGD_REG].def) { |
|---|
| 1395 | 1414 | snd_soc_component_write(component, AIC3X_PLL_PROGC_REG, pll_c); |
|---|
| .. | .. |
|---|
| 1462 | 1481 | static const struct snd_soc_dai_ops aic3x_dai_ops = { |
|---|
| 1463 | 1482 | .hw_params = aic3x_hw_params, |
|---|
| 1464 | 1483 | .prepare = aic3x_prepare, |
|---|
| 1465 | | - .digital_mute = aic3x_mute, |
|---|
| 1484 | + .mute_stream = aic3x_mute, |
|---|
| 1466 | 1485 | .set_sysclk = aic3x_set_dai_sysclk, |
|---|
| 1467 | 1486 | .set_fmt = aic3x_set_dai_fmt, |
|---|
| 1468 | 1487 | .set_tdm_slot = aic3x_set_dai_tdm_slot, |
|---|
| 1488 | + .no_capture_mute = 1, |
|---|
| 1469 | 1489 | }; |
|---|
| 1470 | 1490 | |
|---|
| 1471 | 1491 | static struct snd_soc_dai_driver aic3x_dai = { |
|---|
| .. | .. |
|---|
| 1604 | 1624 | for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) { |
|---|
| 1605 | 1625 | aic3x->disable_nb[i].nb.notifier_call = aic3x_regulator_event; |
|---|
| 1606 | 1626 | aic3x->disable_nb[i].aic3x = aic3x; |
|---|
| 1607 | | - ret = regulator_register_notifier(aic3x->supplies[i].consumer, |
|---|
| 1608 | | - &aic3x->disable_nb[i].nb); |
|---|
| 1627 | + ret = devm_regulator_register_notifier( |
|---|
| 1628 | + aic3x->supplies[i].consumer, |
|---|
| 1629 | + &aic3x->disable_nb[i].nb); |
|---|
| 1609 | 1630 | if (ret) { |
|---|
| 1610 | 1631 | dev_err(component->dev, |
|---|
| 1611 | 1632 | "Failed to request regulator notifier: %d\n", |
|---|
| 1612 | 1633 | ret); |
|---|
| 1613 | | - goto err_notif; |
|---|
| 1634 | + return ret; |
|---|
| 1614 | 1635 | } |
|---|
| 1615 | 1636 | } |
|---|
| 1616 | 1637 | |
|---|
| .. | .. |
|---|
| 1668 | 1689 | aic3x_add_widgets(component); |
|---|
| 1669 | 1690 | |
|---|
| 1670 | 1691 | return 0; |
|---|
| 1671 | | - |
|---|
| 1672 | | -err_notif: |
|---|
| 1673 | | - while (i--) |
|---|
| 1674 | | - regulator_unregister_notifier(aic3x->supplies[i].consumer, |
|---|
| 1675 | | - &aic3x->disable_nb[i].nb); |
|---|
| 1676 | | - return ret; |
|---|
| 1677 | | -} |
|---|
| 1678 | | - |
|---|
| 1679 | | -static void aic3x_remove(struct snd_soc_component *component) |
|---|
| 1680 | | -{ |
|---|
| 1681 | | - struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component); |
|---|
| 1682 | | - int i; |
|---|
| 1683 | | - |
|---|
| 1684 | | - for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) |
|---|
| 1685 | | - regulator_unregister_notifier(aic3x->supplies[i].consumer, |
|---|
| 1686 | | - &aic3x->disable_nb[i].nb); |
|---|
| 1687 | 1692 | } |
|---|
| 1688 | 1693 | |
|---|
| 1689 | 1694 | static const struct snd_soc_component_driver soc_component_dev_aic3x = { |
|---|
| 1690 | 1695 | .set_bias_level = aic3x_set_bias_level, |
|---|
| 1691 | 1696 | .probe = aic3x_probe, |
|---|
| 1692 | | - .remove = aic3x_remove, |
|---|
| 1693 | 1697 | .controls = aic3x_snd_controls, |
|---|
| 1694 | 1698 | .num_controls = ARRAY_SIZE(aic3x_snd_controls), |
|---|
| 1695 | 1699 | .dapm_widgets = aic3x_dapm_widgets, |
|---|